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);
1960 if (leveldir_new->levels < 1)
1961 leveldir_new->levels = 1;
1964 leveldir_new->last_level =
1965 leveldir_new->first_level + leveldir_new->levels - 1;
1967 leveldir_new->in_user_dir =
1968 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1970 /* adjust some settings if user's private level directory was detected */
1971 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
1972 leveldir_new->in_user_dir &&
1973 (strcmp(leveldir_new->subdir, getLoginName()) == 0 ||
1974 strcmp(leveldir_new->name, getLoginName()) == 0 ||
1975 strcmp(leveldir_new->author, getRealName()) == 0))
1977 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
1978 leveldir_new->readonly = FALSE;
1981 leveldir_new->user_defined =
1982 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
1984 leveldir_new->color = LEVELCOLOR(leveldir_new);
1986 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1988 leveldir_new->handicap_level = /* set handicap to default value */
1989 (leveldir_new->user_defined || !leveldir_new->handicap ?
1990 leveldir_new->last_level : leveldir_new->first_level);
1993 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
1995 /* skip level sets without levels (which are probably artwork base sets) */
1997 freeSetupFileHash(setup_file_hash);
1998 free(directory_path);
2005 pushTreeInfo(node_first, leveldir_new);
2007 freeSetupFileHash(setup_file_hash);
2009 if (leveldir_new->level_group)
2011 /* create node to link back to current level directory */
2012 createParentTreeInfoNode(leveldir_new);
2014 /* step into sub-directory and look for more level series */
2015 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2016 leveldir_new, directory_path);
2019 free(directory_path);
2025 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2026 TreeInfo *node_parent,
2027 char *level_directory)
2030 struct dirent *dir_entry;
2031 boolean valid_entry_found = FALSE;
2033 if ((dir = opendir(level_directory)) == NULL)
2035 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2039 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2041 struct stat file_status;
2042 char *directory_name = dir_entry->d_name;
2043 char *directory_path = getPath2(level_directory, directory_name);
2045 /* skip entries for current and parent directory */
2046 if (strcmp(directory_name, ".") == 0 ||
2047 strcmp(directory_name, "..") == 0)
2049 free(directory_path);
2053 /* find out if directory entry is itself a directory */
2054 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2055 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2057 free(directory_path);
2061 free(directory_path);
2063 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2064 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2065 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2068 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2075 if (!valid_entry_found)
2077 /* check if this directory directly contains a file "levelinfo.conf" */
2078 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2079 level_directory, ".");
2082 if (!valid_entry_found)
2083 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2087 void LoadLevelInfo()
2089 InitUserLevelDirectory(getLoginName());
2091 DrawInitText("Loading level series:", 120, FC_GREEN);
2093 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2094 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2096 /* before sorting, the first entries will be from the user directory */
2097 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2099 if (leveldir_first == NULL)
2100 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2102 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2105 dumpTreeInfo(leveldir_first, 0);
2109 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2110 TreeInfo *node_parent,
2111 char *base_directory,
2112 char *directory_name, int type)
2114 char *directory_path = getPath2(base_directory, directory_name);
2115 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2116 SetupFileHash *setup_file_hash = NULL;
2117 TreeInfo *artwork_new = NULL;
2120 if (fileExists(filename))
2121 setup_file_hash = loadSetupFileHash(filename);
2123 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2126 struct dirent *dir_entry;
2127 boolean valid_file_found = FALSE;
2129 if ((dir = opendir(directory_path)) != NULL)
2131 while ((dir_entry = readdir(dir)) != NULL)
2133 char *entry_name = dir_entry->d_name;
2135 if (FileIsArtworkType(entry_name, type))
2137 valid_file_found = TRUE;
2145 if (!valid_file_found)
2147 if (strcmp(directory_name, ".") != 0)
2148 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2150 free(directory_path);
2157 artwork_new = newTreeInfo();
2160 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2162 setTreeInfoToDefaults(artwork_new, type);
2164 artwork_new->subdir = getStringCopy(directory_name);
2166 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2169 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2172 /* set all structure fields according to the token/value pairs */
2174 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2175 setSetupInfo(levelinfo_tokens, i,
2176 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2179 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2180 setString(&artwork_new->name, artwork_new->subdir);
2183 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2186 if (artwork_new->identifier == NULL)
2187 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2189 if (artwork_new->name_sorting == NULL)
2190 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2193 if (node_parent == NULL) /* top level group */
2195 artwork_new->basepath = getStringCopy(base_directory);
2196 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2198 else /* sub level group */
2200 artwork_new->basepath = getStringCopy(node_parent->basepath);
2201 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2204 artwork_new->in_user_dir =
2205 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2207 /* (may use ".sort_priority" from "setup_file_hash" above) */
2208 artwork_new->color = ARTWORKCOLOR(artwork_new);
2210 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2212 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2214 if (strcmp(artwork_new->subdir, ".") == 0)
2216 if (artwork_new->user_defined)
2218 setString(&artwork_new->identifier, "private");
2219 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2223 setString(&artwork_new->identifier, "classic");
2224 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2227 /* set to new values after changing ".sort_priority" */
2228 artwork_new->color = ARTWORKCOLOR(artwork_new);
2230 setString(&artwork_new->class_desc,
2231 getLevelClassDescription(artwork_new));
2235 setString(&artwork_new->identifier, artwork_new->subdir);
2238 setString(&artwork_new->name, artwork_new->identifier);
2239 setString(&artwork_new->name_sorting, artwork_new->name);
2242 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2244 pushTreeInfo(node_first, artwork_new);
2246 freeSetupFileHash(setup_file_hash);
2248 free(directory_path);
2254 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2255 TreeInfo *node_parent,
2256 char *base_directory, int type)
2259 struct dirent *dir_entry;
2260 boolean valid_entry_found = FALSE;
2262 if ((dir = opendir(base_directory)) == NULL)
2264 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2265 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2269 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2271 struct stat file_status;
2272 char *directory_name = dir_entry->d_name;
2273 char *directory_path = getPath2(base_directory, directory_name);
2275 /* skip entries for current and parent directory */
2276 if (strcmp(directory_name, ".") == 0 ||
2277 strcmp(directory_name, "..") == 0)
2279 free(directory_path);
2283 /* find out if directory entry is itself a directory */
2284 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2285 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2287 free(directory_path);
2291 free(directory_path);
2293 /* check if this directory contains artwork with or without config file */
2294 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2296 directory_name, type);
2301 /* check if this directory directly contains artwork itself */
2302 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2303 base_directory, ".",
2305 if (!valid_entry_found)
2306 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2310 static TreeInfo *getDummyArtworkInfo(int type)
2312 /* this is only needed when there is completely no artwork available */
2313 TreeInfo *artwork_new = newTreeInfo();
2315 setTreeInfoToDefaults(artwork_new, type);
2317 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2318 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2319 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2321 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2322 setString(&artwork_new->name, UNDEFINED_FILENAME);
2323 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2328 void LoadArtworkInfo()
2330 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2332 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2333 options.graphics_directory,
2334 TREE_TYPE_GRAPHICS_DIR);
2335 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2336 getUserGraphicsDir(),
2337 TREE_TYPE_GRAPHICS_DIR);
2339 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2340 options.sounds_directory,
2341 TREE_TYPE_SOUNDS_DIR);
2342 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2344 TREE_TYPE_SOUNDS_DIR);
2346 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2347 options.music_directory,
2348 TREE_TYPE_MUSIC_DIR);
2349 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2351 TREE_TYPE_MUSIC_DIR);
2353 if (artwork.gfx_first == NULL)
2354 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2355 if (artwork.snd_first == NULL)
2356 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2357 if (artwork.mus_first == NULL)
2358 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2360 /* before sorting, the first entries will be from the user directory */
2361 artwork.gfx_current =
2362 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2363 if (artwork.gfx_current == NULL)
2364 artwork.gfx_current =
2365 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2366 if (artwork.gfx_current == NULL)
2367 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2369 artwork.snd_current =
2370 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2371 if (artwork.snd_current == NULL)
2372 artwork.snd_current =
2373 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2374 if (artwork.snd_current == NULL)
2375 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2377 artwork.mus_current =
2378 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2379 if (artwork.mus_current == NULL)
2380 artwork.mus_current =
2381 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2382 if (artwork.mus_current == NULL)
2383 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2385 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2386 artwork.snd_current_identifier = artwork.snd_current->identifier;
2387 artwork.mus_current_identifier = artwork.mus_current->identifier;
2390 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2391 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2392 printf("music set == %s\n\n", artwork.mus_current_identifier);
2395 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2396 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2397 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2400 dumpTreeInfo(artwork.gfx_first, 0);
2401 dumpTreeInfo(artwork.snd_first, 0);
2402 dumpTreeInfo(artwork.mus_first, 0);
2406 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2407 LevelDirTree *level_node)
2409 /* recursively check all level directories for artwork sub-directories */
2413 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2414 ARTWORK_DIRECTORY((*artwork_node)->type));
2416 if (!level_node->parent_link)
2418 TreeInfo *topnode_last = *artwork_node;
2420 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2421 (*artwork_node)->type);
2423 if (topnode_last != *artwork_node)
2425 free((*artwork_node)->identifier);
2426 free((*artwork_node)->name);
2427 free((*artwork_node)->name_sorting);
2429 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2430 (*artwork_node)->name = getStringCopy(level_node->name);
2431 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2433 (*artwork_node)->sort_priority = level_node->sort_priority;
2434 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2440 if (level_node->node_group != NULL)
2441 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2443 level_node = level_node->next;
2447 void LoadLevelArtworkInfo()
2449 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2451 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2452 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2453 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2455 /* needed for reloading level artwork not known at ealier stage */
2457 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2459 artwork.gfx_current =
2460 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2461 if (artwork.gfx_current == NULL)
2462 artwork.gfx_current =
2463 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2464 if (artwork.gfx_current == NULL)
2465 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2468 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2470 artwork.snd_current =
2471 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2472 if (artwork.snd_current == NULL)
2473 artwork.snd_current =
2474 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2475 if (artwork.snd_current == NULL)
2476 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2479 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2481 artwork.mus_current =
2482 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2483 if (artwork.mus_current == NULL)
2484 artwork.mus_current =
2485 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2486 if (artwork.mus_current == NULL)
2487 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2490 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2491 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2492 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2495 dumpTreeInfo(artwork.gfx_first, 0);
2496 dumpTreeInfo(artwork.snd_first, 0);
2497 dumpTreeInfo(artwork.mus_first, 0);
2501 static void SaveUserLevelInfo()
2503 LevelDirTree *level_info;
2508 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2510 if (!(file = fopen(filename, MODE_WRITE)))
2512 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2517 level_info = newTreeInfo();
2519 /* always start with reliable default values */
2520 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2522 setString(&level_info->name, getLoginName());
2523 setString(&level_info->author, getRealName());
2524 level_info->levels = 100;
2525 level_info->first_level = 1;
2527 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2529 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2530 getCookie("LEVELINFO")));
2533 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2535 if (i == LEVELINFO_TOKEN_NAME ||
2536 i == LEVELINFO_TOKEN_AUTHOR ||
2537 i == LEVELINFO_TOKEN_LEVELS ||
2538 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2539 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2541 /* just to make things nicer :) */
2542 if (i == LEVELINFO_TOKEN_AUTHOR)
2543 fprintf(file, "\n");
2546 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2550 SetFilePermissions(filename, PERMS_PRIVATE);
2552 freeTreeInfo(level_info);
2556 char *getSetupValue(int type, void *value)
2558 static char value_string[MAX_LINE_LEN];
2566 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2570 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2574 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2578 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2582 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2586 sprintf(value_string, "%d", *(int *)value);
2590 strcpy(value_string, *(char **)value);
2594 value_string[0] = '\0';
2598 return value_string;
2601 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2605 static char token_string[MAX_LINE_LEN];
2606 int token_type = token_info[token_nr].type;
2607 void *setup_value = token_info[token_nr].value;
2608 char *token_text = token_info[token_nr].text;
2609 char *value_string = getSetupValue(token_type, setup_value);
2611 /* build complete token string */
2612 sprintf(token_string, "%s%s", prefix, token_text);
2614 /* build setup entry line */
2615 line = getFormattedSetupEntry(token_string, value_string);
2617 if (token_type == TYPE_KEY_X11)
2619 Key key = *(Key *)setup_value;
2620 char *keyname = getKeyNameFromKey(key);
2622 /* add comment, if useful */
2623 if (strcmp(keyname, "(undefined)") != 0 &&
2624 strcmp(keyname, "(unknown)") != 0)
2626 /* add at least one whitespace */
2628 for (i = strlen(line); i < token_comment_position; i++)
2632 strcat(line, keyname);
2639 void LoadLevelSetup_LastSeries()
2641 /* ----------------------------------------------------------------------- */
2642 /* ~/.<program>/levelsetup.conf */
2643 /* ----------------------------------------------------------------------- */
2645 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2646 SetupFileHash *level_setup_hash = NULL;
2648 /* always start with reliable default values */
2649 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2651 if ((level_setup_hash = loadSetupFileHash(filename)))
2653 char *last_level_series =
2654 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2656 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2658 if (leveldir_current == NULL)
2659 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2661 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2663 freeSetupFileHash(level_setup_hash);
2666 Error(ERR_WARN, "using default setup values");
2671 void SaveLevelSetup_LastSeries()
2673 /* ----------------------------------------------------------------------- */
2674 /* ~/.<program>/levelsetup.conf */
2675 /* ----------------------------------------------------------------------- */
2677 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2678 char *level_subdir = leveldir_current->subdir;
2681 InitUserDataDirectory();
2683 if (!(file = fopen(filename, MODE_WRITE)))
2685 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2690 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2691 getCookie("LEVELSETUP")));
2692 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2697 SetFilePermissions(filename, PERMS_PRIVATE);
2702 static void checkSeriesInfo()
2704 static char *level_directory = NULL;
2706 struct dirent *dir_entry;
2708 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2710 level_directory = getPath2((leveldir_current->in_user_dir ?
2711 getUserLevelDir(NULL) :
2712 options.level_directory),
2713 leveldir_current->fullpath);
2715 if ((dir = opendir(level_directory)) == NULL)
2717 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2721 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2723 if (strlen(dir_entry->d_name) > 4 &&
2724 dir_entry->d_name[3] == '.' &&
2725 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2727 char levelnum_str[4];
2730 strncpy(levelnum_str, dir_entry->d_name, 3);
2731 levelnum_str[3] = '\0';
2733 levelnum_value = atoi(levelnum_str);
2736 if (levelnum_value < leveldir_current->first_level)
2738 Error(ERR_WARN, "additional level %d found", levelnum_value);
2739 leveldir_current->first_level = levelnum_value;
2741 else if (levelnum_value > leveldir_current->last_level)
2743 Error(ERR_WARN, "additional level %d found", levelnum_value);
2744 leveldir_current->last_level = levelnum_value;
2753 void LoadLevelSetup_SeriesInfo()
2756 SetupFileHash *level_setup_hash = NULL;
2757 char *level_subdir = leveldir_current->subdir;
2759 /* always start with reliable default values */
2760 level_nr = leveldir_current->first_level;
2762 checkSeriesInfo(leveldir_current);
2764 /* ----------------------------------------------------------------------- */
2765 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2766 /* ----------------------------------------------------------------------- */
2768 level_subdir = leveldir_current->subdir;
2770 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2772 if ((level_setup_hash = loadSetupFileHash(filename)))
2776 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2780 level_nr = atoi(token_value);
2782 if (level_nr < leveldir_current->first_level)
2783 level_nr = leveldir_current->first_level;
2784 if (level_nr > leveldir_current->last_level)
2785 level_nr = leveldir_current->last_level;
2788 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2792 int level_nr = atoi(token_value);
2794 if (level_nr < leveldir_current->first_level)
2795 level_nr = leveldir_current->first_level;
2796 if (level_nr > leveldir_current->last_level + 1)
2797 level_nr = leveldir_current->last_level;
2799 if (leveldir_current->user_defined || !leveldir_current->handicap)
2800 level_nr = leveldir_current->last_level;
2802 leveldir_current->handicap_level = level_nr;
2805 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2807 freeSetupFileHash(level_setup_hash);
2810 Error(ERR_WARN, "using default setup values");
2815 void SaveLevelSetup_SeriesInfo()
2818 char *level_subdir = leveldir_current->subdir;
2819 char *level_nr_str = int2str(level_nr, 0);
2820 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2823 /* ----------------------------------------------------------------------- */
2824 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2825 /* ----------------------------------------------------------------------- */
2827 InitLevelSetupDirectory(level_subdir);
2829 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2831 if (!(file = fopen(filename, MODE_WRITE)))
2833 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2838 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2839 getCookie("LEVELSETUP")));
2840 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2842 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2843 handicap_level_str));
2847 SetFilePermissions(filename, PERMS_PRIVATE);