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_GREEN : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
49 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
52 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
53 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
54 IS_LEVELCLASS_BD(n) ? 2 : \
55 IS_LEVELCLASS_EM(n) ? 3 : \
56 IS_LEVELCLASS_SP(n) ? 4 : \
57 IS_LEVELCLASS_DX(n) ? 5 : \
58 IS_LEVELCLASS_CONTRIB(n) ? 6 : \
59 IS_LEVELCLASS_PRIVATE(n) ? 7 : \
62 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
63 IS_ARTWORKCLASS_CONTRIB(n) ? FC_YELLOW : \
64 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
65 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
68 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
69 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
70 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
71 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
74 #define TOKEN_VALUE_POSITION 40
75 #define TOKEN_COMMENT_POSITION 60
77 #define MAX_COOKIE_LEN 256
80 /* ------------------------------------------------------------------------- */
82 /* ------------------------------------------------------------------------- */
84 static char *getLevelClassDescription(TreeInfo *ldi)
86 int position = ldi->sort_priority / 100;
88 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
89 return levelclass_desc[position];
91 return "Unknown Level Class";
94 static char *getUserLevelDir(char *level_subdir)
96 static char *userlevel_dir = NULL;
97 char *data_dir = getUserDataDir();
98 char *userlevel_subdir = LEVELS_DIRECTORY;
103 if (level_subdir != NULL)
104 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
106 userlevel_dir = getPath2(data_dir, userlevel_subdir);
108 return userlevel_dir;
111 static char *getTapeDir(char *level_subdir)
113 static char *tape_dir = NULL;
114 char *data_dir = getUserDataDir();
115 char *tape_subdir = TAPES_DIRECTORY;
120 if (level_subdir != NULL)
121 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
123 tape_dir = getPath2(data_dir, tape_subdir);
128 static char *getScoreDir(char *level_subdir)
130 static char *score_dir = NULL;
131 char *data_dir = getCommonDataDir();
132 char *score_subdir = SCORES_DIRECTORY;
137 if (level_subdir != NULL)
138 score_dir = getPath3(data_dir, score_subdir, level_subdir);
140 score_dir = getPath2(data_dir, score_subdir);
145 static char *getLevelSetupDir(char *level_subdir)
147 static char *levelsetup_dir = NULL;
148 char *data_dir = getUserDataDir();
149 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
152 free(levelsetup_dir);
154 if (level_subdir != NULL)
155 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
157 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
159 return levelsetup_dir;
162 static char *getLevelDirFromTreeInfo(TreeInfo *node)
164 static char *level_dir = NULL;
167 return options.level_directory;
172 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
173 options.level_directory), node->fullpath);
178 static char *getCurrentLevelDir()
180 return getLevelDirFromTreeInfo(leveldir_current);
183 static char *getDefaultGraphicsDir(char *graphics_subdir)
185 static char *graphics_dir = NULL;
187 if (graphics_subdir == NULL)
188 return options.graphics_directory;
193 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
198 static char *getDefaultSoundsDir(char *sounds_subdir)
200 static char *sounds_dir = NULL;
202 if (sounds_subdir == NULL)
203 return options.sounds_directory;
208 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
213 static char *getDefaultMusicDir(char *music_subdir)
215 static char *music_dir = NULL;
217 if (music_subdir == NULL)
218 return options.music_directory;
223 music_dir = getPath2(options.music_directory, music_subdir);
228 static char *getDefaultArtworkSet(int type)
230 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
231 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
232 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
235 static char *getDefaultArtworkDir(int type)
237 return (type == TREE_TYPE_GRAPHICS_DIR ?
238 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
239 type == TREE_TYPE_SOUNDS_DIR ?
240 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
241 type == TREE_TYPE_MUSIC_DIR ?
242 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
245 static char *getUserGraphicsDir()
247 static char *usergraphics_dir = NULL;
249 if (usergraphics_dir == NULL)
250 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
252 return usergraphics_dir;
255 static char *getUserSoundsDir()
257 static char *usersounds_dir = NULL;
259 if (usersounds_dir == NULL)
260 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
262 return usersounds_dir;
265 static char *getUserMusicDir()
267 static char *usermusic_dir = NULL;
269 if (usermusic_dir == NULL)
270 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
272 return usermusic_dir;
275 static char *getSetupArtworkDir(TreeInfo *ti)
277 static char *artwork_dir = NULL;
279 if (artwork_dir != NULL)
282 artwork_dir = getPath2(ti->basepath, ti->fullpath);
287 char *setLevelArtworkDir(TreeInfo *ti)
289 char **artwork_path_ptr, **artwork_set_ptr;
290 TreeInfo *level_artwork;
292 if (ti == NULL || leveldir_current == NULL)
295 artwork_path_ptr = &(LEVELDIR_ARTWORK_PATH(leveldir_current, ti->type));
296 artwork_set_ptr = &(LEVELDIR_ARTWORK_SET( leveldir_current, ti->type));
298 if (*artwork_path_ptr != NULL)
299 free(*artwork_path_ptr);
301 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
302 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
305 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
306 normally result in using the artwork configured in the setup menu. But
307 if an artwork subdirectory exists (which might contain custom artwork
308 or an artwork configuration file), this level artwork must be treated
309 as relative to the default "classic" artwork, not to the artwork that
310 is currently configured in the setup menu. */
312 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
314 if (*artwork_set_ptr != NULL)
315 free(*artwork_set_ptr);
319 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
320 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
324 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
325 *artwork_set_ptr = NULL;
331 return *artwork_set_ptr;
334 inline static char *getLevelArtworkSet(int type)
336 if (leveldir_current == NULL)
339 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
342 inline static char *getLevelArtworkDir(int type)
344 if (leveldir_current == NULL)
345 return UNDEFINED_FILENAME;
347 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
350 char *getLevelFilename(int nr)
352 static char *filename = NULL;
353 char basename[MAX_FILENAME_LEN];
355 if (filename != NULL)
359 sprintf(basename, "template.%s", LEVELFILE_EXTENSION);
361 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
363 filename = getPath2(getCurrentLevelDir(), basename);
368 char *getTapeFilename(int nr)
370 static char *filename = NULL;
371 char basename[MAX_FILENAME_LEN];
373 if (filename != NULL)
376 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
377 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
382 char *getScoreFilename(int nr)
384 static char *filename = NULL;
385 char basename[MAX_FILENAME_LEN];
387 if (filename != NULL)
390 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
391 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
396 char *getSetupFilename()
398 static char *filename = NULL;
400 if (filename != NULL)
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 if (filename != NULL)
415 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
420 char *getElementInfoFilename()
422 static char *filename = NULL;
424 if (filename != NULL)
427 filename = getPath2(getCurrentLevelDir(), ELEMENTINFO_FILENAME);
432 static char *getCorrectedArtworkBasename(char *basename)
434 char *basename_corrected = basename;
436 #if defined(PLATFORM_MSDOS)
437 if (program.filename_prefix != NULL)
439 int prefix_len = strlen(program.filename_prefix);
441 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
442 basename_corrected = &basename[prefix_len];
444 /* if corrected filename is still longer than standard MS-DOS filename
445 size (8 characters + 1 dot + 3 characters file extension), shorten
446 filename by writing file extension after 8th basename character */
447 if (strlen(basename_corrected) > 8 + 1 + 3)
449 static char *msdos_filename = NULL;
451 if (msdos_filename != NULL)
452 free(msdos_filename);
454 msdos_filename = getStringCopy(basename_corrected);
455 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
457 basename_corrected = msdos_filename;
462 return basename_corrected;
465 char *getCustomImageFilename(char *basename)
467 static char *filename = NULL;
468 boolean skip_setup_artwork = FALSE;
470 if (filename != NULL)
473 basename = getCorrectedArtworkBasename(basename);
475 if (!setup.override_level_graphics)
477 /* 1st try: look for special artwork in current level series directory */
478 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
479 if (fileExists(filename))
484 /* check if there is special artwork configured in level series config */
485 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
487 /* 2nd try: look for special artwork configured in level series config */
488 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
489 if (fileExists(filename))
494 /* take missing artwork configured in level set config from default */
495 skip_setup_artwork = TRUE;
499 if (!skip_setup_artwork)
501 /* 3rd try: look for special artwork in configured artwork directory */
502 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
503 if (fileExists(filename))
509 /* 4th try: look for default artwork in new default artwork directory */
510 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
511 if (fileExists(filename))
516 /* 5th try: look for default artwork in old default artwork directory */
517 filename = getPath2(options.graphics_directory, basename);
518 if (fileExists(filename))
521 return NULL; /* cannot find specified artwork file anywhere */
524 char *getCustomSoundFilename(char *basename)
526 static char *filename = NULL;
527 boolean skip_setup_artwork = FALSE;
529 if (filename != NULL)
532 basename = getCorrectedArtworkBasename(basename);
534 if (!setup.override_level_sounds)
536 /* 1st try: look for special artwork in current level series directory */
537 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
538 if (fileExists(filename))
543 /* check if there is special artwork configured in level series config */
544 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
546 /* 2nd try: look for special artwork configured in level series config */
547 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
548 if (fileExists(filename))
553 /* take missing artwork configured in level set config from default */
554 skip_setup_artwork = TRUE;
558 if (!skip_setup_artwork)
560 /* 3rd try: look for special artwork in configured artwork directory */
561 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
562 if (fileExists(filename))
568 /* 4th try: look for default artwork in new default artwork directory */
569 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
570 if (fileExists(filename))
575 /* 5th try: look for default artwork in old default artwork directory */
576 filename = getPath2(options.sounds_directory, basename);
577 if (fileExists(filename))
580 return NULL; /* cannot find specified artwork file anywhere */
583 char *getCustomMusicFilename(char *basename)
585 static char *filename = NULL;
586 boolean skip_setup_artwork = FALSE;
588 if (filename != NULL)
591 basename = getCorrectedArtworkBasename(basename);
593 if (!setup.override_level_music)
595 /* 1st try: look for special artwork in current level series directory */
596 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
597 if (fileExists(filename))
602 /* check if there is special artwork configured in level series config */
603 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
605 /* 2nd try: look for special artwork configured in level series config */
606 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
607 if (fileExists(filename))
612 /* take missing artwork configured in level set config from default */
613 skip_setup_artwork = TRUE;
617 if (!skip_setup_artwork)
619 /* 3rd try: look for special artwork in configured artwork directory */
620 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
621 if (fileExists(filename))
627 /* 4th try: look for default artwork in new default artwork directory */
628 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
629 if (fileExists(filename))
634 /* 5th try: look for default artwork in old default artwork directory */
635 filename = getPath2(options.music_directory, basename);
636 if (fileExists(filename))
639 return NULL; /* cannot find specified artwork file anywhere */
642 char *getCustomArtworkFilename(char *basename, int type)
644 if (type == ARTWORK_TYPE_GRAPHICS)
645 return getCustomImageFilename(basename);
646 else if (type == ARTWORK_TYPE_SOUNDS)
647 return getCustomSoundFilename(basename);
648 else if (type == ARTWORK_TYPE_MUSIC)
649 return getCustomMusicFilename(basename);
651 return UNDEFINED_FILENAME;
654 char *getCustomArtworkConfigFilename(int type)
656 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
659 char *getCustomArtworkLevelConfigFilename(int type)
661 static char *filename = NULL;
663 if (filename != NULL)
666 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
671 char *getCustomMusicDirectory(void)
673 static char *directory = NULL;
674 boolean skip_setup_artwork = FALSE;
676 if (directory != NULL)
679 if (!setup.override_level_music)
681 /* 1st try: look for special artwork in current level series directory */
682 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
683 if (fileExists(directory))
688 /* check if there is special artwork configured in level series config */
689 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
691 /* 2nd try: look for special artwork configured in level series config */
692 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
693 if (fileExists(directory))
698 /* take missing artwork configured in level set config from default */
699 skip_setup_artwork = TRUE;
703 if (!skip_setup_artwork)
705 /* 3rd try: look for special artwork in configured artwork directory */
706 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
707 if (fileExists(directory))
713 /* 4th try: look for default artwork in new default artwork directory */
714 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
715 if (fileExists(directory))
720 /* 5th try: look for default artwork in old default artwork directory */
721 directory = getStringCopy(options.music_directory);
722 if (fileExists(directory))
725 return NULL; /* cannot find specified artwork file anywhere */
728 void InitTapeDirectory(char *level_subdir)
730 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
731 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
732 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
735 void InitScoreDirectory(char *level_subdir)
737 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
738 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
739 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
742 static void SaveUserLevelInfo();
744 void InitUserLevelDirectory(char *level_subdir)
746 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
748 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
749 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
750 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
756 void InitLevelSetupDirectory(char *level_subdir)
758 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
759 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
760 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
764 /* ------------------------------------------------------------------------- */
765 /* some functions to handle lists of level directories */
766 /* ------------------------------------------------------------------------- */
768 TreeInfo *newTreeInfo()
770 return checked_calloc(sizeof(TreeInfo));
773 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
775 node_new->next = *node_first;
776 *node_first = node_new;
779 int numTreeInfo(TreeInfo *node)
792 boolean validLevelSeries(TreeInfo *node)
794 return (node != NULL && !node->node_group && !node->parent_link);
797 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
802 if (node->node_group) /* enter level group (step down into tree) */
803 return getFirstValidTreeInfoEntry(node->node_group);
804 else if (node->parent_link) /* skip start entry of level group */
806 if (node->next) /* get first real level series entry */
807 return getFirstValidTreeInfoEntry(node->next);
808 else /* leave empty level group and go on */
809 return getFirstValidTreeInfoEntry(node->node_parent->next);
811 else /* this seems to be a regular level series */
815 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
820 if (node->node_parent == NULL) /* top level group */
821 return *node->node_top;
822 else /* sub level group */
823 return node->node_parent->node_group;
826 int numTreeInfoInGroup(TreeInfo *node)
828 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
831 int posTreeInfo(TreeInfo *node)
833 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
838 if (node_cmp == node)
842 node_cmp = node_cmp->next;
848 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
850 TreeInfo *node_default = node;
865 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
867 if (identifier == NULL)
872 if (node->node_group)
874 TreeInfo *node_group;
876 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
881 else if (!node->parent_link)
883 if (strcmp(identifier, node->identifier) == 0)
893 void dumpTreeInfo(TreeInfo *node, int depth)
897 printf("Dumping TreeInfo:\n");
901 for (i=0; i<(depth + 1) * 3; i++)
905 printf("filename == '%s' ['%s', '%s'] [%d])\n",
906 node->filename, node->fullpath, node->basepath, node->user_defined);
908 printf("filename == '%s' (%s) [%s] (%d)\n",
909 node->filename, node->name, node->identifier, node->sort_priority);
912 if (node->node_group != NULL)
913 dumpTreeInfo(node->node_group, depth + 1);
919 void sortTreeInfo(TreeInfo **node_first,
920 int (*compare_function)(const void *, const void *))
922 int num_nodes = numTreeInfo(*node_first);
923 TreeInfo **sort_array;
924 TreeInfo *node = *node_first;
930 /* allocate array for sorting structure pointers */
931 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
933 /* writing structure pointers to sorting array */
934 while (i < num_nodes && node) /* double boundary check... */
936 sort_array[i] = node;
942 /* sorting the structure pointers in the sorting array */
943 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
946 /* update the linkage of list elements with the sorted node array */
947 for (i=0; i<num_nodes - 1; i++)
948 sort_array[i]->next = sort_array[i + 1];
949 sort_array[num_nodes - 1]->next = NULL;
951 /* update the linkage of the main list anchor pointer */
952 *node_first = sort_array[0];
956 /* now recursively sort the level group structures */
960 if (node->node_group != NULL)
961 sortTreeInfo(&node->node_group, compare_function);
968 /* ========================================================================= */
969 /* some stuff from "files.c" */
970 /* ========================================================================= */
972 #if defined(PLATFORM_WIN32)
974 #define S_IRGRP S_IRUSR
977 #define S_IROTH S_IRUSR
980 #define S_IWGRP S_IWUSR
983 #define S_IWOTH S_IWUSR
986 #define S_IXGRP S_IXUSR
989 #define S_IXOTH S_IXUSR
992 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
997 #endif /* PLATFORM_WIN32 */
999 /* file permissions for newly written files */
1000 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1001 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1002 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1004 #define MODE_W_PRIVATE (S_IWUSR)
1005 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1006 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1008 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1009 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1011 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1012 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1014 char *getUserDataDir(void)
1016 static char *userdata_dir = NULL;
1018 if (userdata_dir == NULL)
1019 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1021 return userdata_dir;
1024 char *getCommonDataDir(void)
1026 static char *common_data_dir = NULL;
1028 #if defined(PLATFORM_WIN32)
1029 if (common_data_dir == NULL)
1031 char *dir = checked_malloc(MAX_PATH + 1);
1033 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1034 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1035 common_data_dir = getPath2(dir, program.userdata_directory);
1037 common_data_dir = options.rw_base_directory;
1040 if (common_data_dir == NULL)
1041 common_data_dir = options.rw_base_directory;
1044 return common_data_dir;
1049 return getUserDataDir();
1052 static mode_t posix_umask(mode_t mask)
1054 #if defined(PLATFORM_UNIX)
1061 static int posix_mkdir(const char *pathname, mode_t mode)
1063 #if defined(PLATFORM_WIN32)
1064 return mkdir(pathname);
1066 return mkdir(pathname, mode);
1070 void createDirectory(char *dir, char *text, int permission_class)
1072 /* leave "other" permissions in umask untouched, but ensure group parts
1073 of USERDATA_DIR_MODE are not masked */
1074 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1075 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1076 mode_t normal_umask = posix_umask(0);
1077 mode_t group_umask = ~(dir_mode & S_IRWXG);
1078 posix_umask(normal_umask & group_umask);
1080 if (access(dir, F_OK) != 0)
1081 if (posix_mkdir(dir, dir_mode) != 0)
1082 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1084 posix_umask(normal_umask); /* reset normal umask */
1087 void InitUserDataDirectory()
1089 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1092 void SetFilePermissions(char *filename, int permission_class)
1094 chmod(filename, (permission_class == PERMS_PRIVATE ?
1095 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1098 char *getCookie(char *file_type)
1100 static char cookie[MAX_COOKIE_LEN + 1];
1102 if (strlen(program.cookie_prefix) + 1 +
1103 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1104 return "[COOKIE ERROR]"; /* should never happen */
1106 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1107 program.cookie_prefix, file_type,
1108 program.version_major, program.version_minor);
1113 int getFileVersionFromCookieString(const char *cookie)
1115 const char *ptr_cookie1, *ptr_cookie2;
1116 const char *pattern1 = "_FILE_VERSION_";
1117 const char *pattern2 = "?.?";
1118 const int len_cookie = strlen(cookie);
1119 const int len_pattern1 = strlen(pattern1);
1120 const int len_pattern2 = strlen(pattern2);
1121 const int len_pattern = len_pattern1 + len_pattern2;
1122 int version_major, version_minor;
1124 if (len_cookie <= len_pattern)
1127 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1128 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1130 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1133 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1134 ptr_cookie2[1] != '.' ||
1135 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1138 version_major = ptr_cookie2[0] - '0';
1139 version_minor = ptr_cookie2[2] - '0';
1141 return VERSION_IDENT(version_major, version_minor, 0, 0);
1144 boolean checkCookieString(const char *cookie, const char *template)
1146 const char *pattern = "_FILE_VERSION_?.?";
1147 const int len_cookie = strlen(cookie);
1148 const int len_template = strlen(template);
1149 const int len_pattern = strlen(pattern);
1151 if (len_cookie != len_template)
1154 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1160 /* ------------------------------------------------------------------------- */
1161 /* setup file list and hash handling functions */
1162 /* ------------------------------------------------------------------------- */
1164 char *getFormattedSetupEntry(char *token, char *value)
1167 static char entry[MAX_LINE_LEN];
1169 /* start with the token and some spaces to format output line */
1170 sprintf(entry, "%s:", token);
1171 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1174 /* continue with the token's value */
1175 strcat(entry, value);
1180 SetupFileList *newSetupFileList(char *token, char *value)
1182 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1184 new->token = getStringCopy(token);
1185 new->value = getStringCopy(value);
1192 void freeSetupFileList(SetupFileList *list)
1202 freeSetupFileList(list->next);
1206 char *getListEntry(SetupFileList *list, char *token)
1211 if (strcmp(list->token, token) == 0)
1214 return getListEntry(list->next, token);
1217 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1222 if (strcmp(list->token, token) == 0)
1227 list->value = getStringCopy(value);
1231 else if (list->next == NULL)
1232 return (list->next = newSetupFileList(token, value));
1234 return setListEntry(list->next, token, value);
1237 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1242 if (list->next == NULL)
1243 return (list->next = newSetupFileList(token, value));
1245 return addListEntry(list->next, token, value);
1249 static void printSetupFileList(SetupFileList *list)
1254 printf("token: '%s'\n", list->token);
1255 printf("value: '%s'\n", list->value);
1257 printSetupFileList(list->next);
1262 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1263 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1264 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1265 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1267 #define insert_hash_entry hashtable_insert
1268 #define search_hash_entry hashtable_search
1269 #define change_hash_entry hashtable_change
1270 #define remove_hash_entry hashtable_remove
1273 static unsigned int get_hash_from_key(void *key)
1278 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1279 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1280 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1281 it works better than many other constants, prime or not) has never been
1282 adequately explained.
1284 If you just want to have a good hash function, and cannot wait, djb2
1285 is one of the best string hash functions i know. It has excellent
1286 distribution and speed on many different sets of keys and table sizes.
1287 You are not likely to do better with one of the "well known" functions
1288 such as PJW, K&R, etc.
1290 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1293 char *str = (char *)key;
1294 unsigned int hash = 5381;
1297 while ((c = *str++))
1298 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1303 static int keys_are_equal(void *key1, void *key2)
1305 return (strcmp((char *)key1, (char *)key2) == 0);
1308 SetupFileHash *newSetupFileHash()
1310 SetupFileHash *new_hash =
1311 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1313 if (new_hash == NULL)
1314 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1319 void freeSetupFileHash(SetupFileHash *hash)
1324 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1327 char *getHashEntry(SetupFileHash *hash, char *token)
1332 return search_hash_entry(hash, token);
1335 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1342 value_copy = getStringCopy(value);
1344 /* change value; if it does not exist, insert it as new */
1345 if (!change_hash_entry(hash, token, value_copy))
1346 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1347 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1352 static void printSetupFileHash(SetupFileHash *hash)
1354 BEGIN_HASH_ITERATION(hash, itr)
1356 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1357 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1359 END_HASH_ITERATION(hash, itr)
1364 static void *loadSetupFileData(char *filename, boolean use_hash)
1367 char line[MAX_LINE_LEN];
1368 char *token, *value, *line_ptr;
1369 void *setup_file_data, *insert_ptr = NULL;
1373 setup_file_data = newSetupFileHash();
1375 insert_ptr = setup_file_data = newSetupFileList("", "");
1377 if (!(file = fopen(filename, MODE_READ)))
1379 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1385 /* read next line of input file */
1386 if (!fgets(line, MAX_LINE_LEN, file))
1389 /* cut trailing comment or whitespace from input line */
1390 for (line_ptr = line; *line_ptr; line_ptr++)
1392 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1399 /* cut trailing whitespaces from input line */
1400 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1401 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1404 /* ignore empty lines */
1408 line_len = strlen(line);
1410 /* cut leading whitespaces from token */
1411 for (token = line; *token; token++)
1412 if (*token != ' ' && *token != '\t')
1415 /* find end of token */
1416 for (line_ptr = token; *line_ptr; line_ptr++)
1418 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1425 if (line_ptr < line + line_len)
1426 value = line_ptr + 1;
1429 value = "true"; /* treat tokens without value as "true" */
1434 /* cut leading whitespaces from value */
1435 for (; *value; value++)
1436 if (*value != ' ' && *value != '\t')
1439 if (*token && *value)
1442 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1444 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1452 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1453 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1457 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1458 SetupFileList *first_valid_list_entry = setup_file_list->next;
1460 /* free empty list header */
1461 setup_file_list->next = NULL;
1462 freeSetupFileList(setup_file_list);
1463 setup_file_data = first_valid_list_entry;
1465 if (first_valid_list_entry == NULL)
1466 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1469 return setup_file_data;
1472 SetupFileList *loadSetupFileList(char *filename)
1474 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1477 SetupFileHash *loadSetupFileHash(char *filename)
1479 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1482 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1485 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1488 Error(ERR_WARN, "configuration file has no file identifier");
1489 else if (!checkCookieString(value, identifier))
1490 Error(ERR_WARN, "configuration file has wrong file identifier");
1494 /* ========================================================================= */
1495 /* setup file stuff */
1496 /* ========================================================================= */
1498 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1499 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1500 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1502 /* level directory info */
1503 #define LEVELINFO_TOKEN_IDENTIFIER 0
1504 #define LEVELINFO_TOKEN_NAME 1
1505 #define LEVELINFO_TOKEN_NAME_SORTING 2
1506 #define LEVELINFO_TOKEN_AUTHOR 3
1507 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1508 #define LEVELINFO_TOKEN_LEVELS 5
1509 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1510 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1511 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1512 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1513 #define LEVELINFO_TOKEN_READONLY 10
1514 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1515 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1516 #define LEVELINFO_TOKEN_MUSIC_SET 13
1518 #define NUM_LEVELINFO_TOKENS 14
1520 static LevelDirTree ldi;
1522 static struct TokenInfo levelinfo_tokens[] =
1524 /* level directory info */
1525 { TYPE_STRING, &ldi.identifier, "identifier" },
1526 { TYPE_STRING, &ldi.name, "name" },
1527 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1528 { TYPE_STRING, &ldi.author, "author" },
1529 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1530 { TYPE_INTEGER, &ldi.levels, "levels" },
1531 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1532 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1533 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1534 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1535 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1536 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1537 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1538 { TYPE_STRING, &ldi.music_set, "music_set" }
1541 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1545 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1546 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1547 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1548 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1551 ldi->node_parent = NULL;
1552 ldi->node_group = NULL;
1556 ldi->cl_cursor = -1;
1558 ldi->filename = NULL;
1559 ldi->fullpath = NULL;
1560 ldi->basepath = NULL;
1561 ldi->identifier = NULL;
1562 ldi->name = getStringCopy(ANONYMOUS_NAME);
1563 ldi->name_sorting = NULL;
1564 ldi->author = getStringCopy(ANONYMOUS_NAME);
1566 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1567 ldi->latest_engine = FALSE; /* default: get from level */
1568 ldi->parent_link = FALSE;
1569 ldi->user_defined = FALSE;
1571 ldi->class_desc = NULL;
1573 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1575 ldi->imported_from = NULL;
1577 ldi->graphics_set = NULL;
1578 ldi->sounds_set = NULL;
1579 ldi->music_set = NULL;
1580 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1581 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1582 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1585 ldi->first_level = 0;
1586 ldi->last_level = 0;
1587 ldi->level_group = FALSE;
1588 ldi->handicap_level = 0;
1589 ldi->readonly = TRUE;
1593 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1597 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1599 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1605 /* copy all values from the parent structure */
1607 ldi->type = parent->type;
1609 ldi->node_top = parent->node_top;
1610 ldi->node_parent = parent;
1611 ldi->node_group = NULL;
1615 ldi->cl_cursor = -1;
1617 ldi->filename = NULL;
1618 ldi->fullpath = NULL;
1619 ldi->basepath = NULL;
1620 ldi->identifier = NULL;
1621 ldi->name = getStringCopy(ANONYMOUS_NAME);
1622 ldi->name_sorting = NULL;
1623 ldi->author = getStringCopy(parent->author);
1625 ldi->sort_priority = parent->sort_priority;
1626 ldi->latest_engine = parent->latest_engine;
1627 ldi->parent_link = FALSE;
1628 ldi->user_defined = parent->user_defined;
1629 ldi->color = parent->color;
1630 ldi->class_desc = getStringCopy(parent->class_desc);
1632 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1634 ldi->imported_from = getStringCopy(parent->imported_from);
1636 ldi->graphics_set = NULL;
1637 ldi->sounds_set = NULL;
1638 ldi->music_set = NULL;
1639 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1640 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1641 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1644 ldi->first_level = 0;
1645 ldi->last_level = 0;
1646 ldi->level_group = FALSE;
1647 ldi->handicap_level = 0;
1648 ldi->readonly = TRUE;
1654 /* first copy all values from the parent structure ... */
1657 /* ... then set all fields to default that cannot be inherited from parent.
1658 This is especially important for all those fields that can be set from
1659 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1660 calls 'free()' for all already set token values which requires that no
1661 other structure's pointer may point to them!
1664 ldi->filename = NULL;
1665 ldi->fullpath = NULL;
1666 ldi->basepath = NULL;
1667 ldi->identifier = NULL;
1668 ldi->name = getStringCopy(ANONYMOUS_NAME);
1669 ldi->name_sorting = NULL;
1670 ldi->author = getStringCopy(parent->author);
1672 ldi->imported_from = getStringCopy(parent->imported_from);
1673 ldi->class_desc = getStringCopy(parent->class_desc);
1675 ldi->graphics_set = NULL;
1676 ldi->sounds_set = NULL;
1677 ldi->music_set = NULL;
1678 ldi->graphics_path = NULL;
1679 ldi->sounds_path = NULL;
1680 ldi->music_path = NULL;
1682 ldi->level_group = FALSE;
1683 ldi->parent_link = FALSE;
1685 ldi->node_top = parent->node_top;
1686 ldi->node_parent = parent;
1687 ldi->node_group = NULL;
1693 static void freeTreeInfo(TreeInfo *ldi)
1696 free(ldi->filename);
1698 free(ldi->fullpath);
1700 free(ldi->basepath);
1701 if (ldi->identifier)
1702 free(ldi->identifier);
1706 if (ldi->name_sorting)
1707 free(ldi->name_sorting);
1711 if (ldi->class_desc)
1712 free(ldi->class_desc);
1714 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1716 if (ldi->graphics_set)
1717 free(ldi->graphics_set);
1718 if (ldi->sounds_set)
1719 free(ldi->sounds_set);
1721 free(ldi->music_set);
1723 if (ldi->graphics_path)
1724 free(ldi->graphics_path);
1725 if (ldi->sounds_path)
1726 free(ldi->sounds_path);
1727 if (ldi->music_path)
1728 free(ldi->music_path);
1732 void setSetupInfo(struct TokenInfo *token_info,
1733 int token_nr, char *token_value)
1735 int token_type = token_info[token_nr].type;
1736 void *setup_value = token_info[token_nr].value;
1738 if (token_value == NULL)
1741 /* set setup field to corresponding token value */
1746 *(boolean *)setup_value = get_boolean_from_string(token_value);
1750 *(Key *)setup_value = getKeyFromKeyName(token_value);
1754 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1758 *(int *)setup_value = get_integer_from_string(token_value);
1762 if (*(char **)setup_value != NULL)
1763 free(*(char **)setup_value);
1764 *(char **)setup_value = getStringCopy(token_value);
1772 static int compareTreeInfoEntries(const void *object1, const void *object2)
1774 const TreeInfo *entry1 = *((TreeInfo **)object1);
1775 const TreeInfo *entry2 = *((TreeInfo **)object2);
1776 int class_sorting1, class_sorting2;
1779 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1781 class_sorting1 = LEVELSORTING(entry1);
1782 class_sorting2 = LEVELSORTING(entry2);
1786 class_sorting1 = ARTWORKSORTING(entry1);
1787 class_sorting2 = ARTWORKSORTING(entry2);
1790 if (entry1->parent_link || entry2->parent_link)
1791 compare_result = (entry1->parent_link ? -1 : +1);
1792 else if (entry1->sort_priority == entry2->sort_priority)
1794 char *name1 = getStringToLower(entry1->name_sorting);
1795 char *name2 = getStringToLower(entry2->name_sorting);
1797 compare_result = strcmp(name1, name2);
1802 else if (class_sorting1 == class_sorting2)
1803 compare_result = entry1->sort_priority - entry2->sort_priority;
1805 compare_result = class_sorting1 - class_sorting2;
1807 return compare_result;
1810 static void createParentTreeInfoNode(TreeInfo *node_parent)
1814 if (node_parent == NULL)
1817 ti_new = newTreeInfo();
1818 setTreeInfoToDefaults(ti_new, node_parent->type);
1820 ti_new->node_parent = node_parent;
1821 ti_new->parent_link = TRUE;
1824 setString(&ti_new->identifier, node_parent->identifier);
1825 setString(&ti_new->name, ".. (parent directory)");
1826 setString(&ti_new->name_sorting, ti_new->name);
1828 setString(&ti_new->filename, "..");
1829 setString(&ti_new->fullpath, node_parent->fullpath);
1831 ti_new->sort_priority = node_parent->sort_priority;
1832 ti_new->latest_engine = node_parent->latest_engine;
1834 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1836 ti_new->identifier = getStringCopy(node_parent->identifier);
1837 ti_new->name = ".. (parent directory)";
1838 ti_new->name_sorting = getStringCopy(ti_new->name);
1840 ti_new->filename = "..";
1841 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1843 ti_new->sort_priority = node_parent->sort_priority;
1844 ti_new->latest_engine = node_parent->latest_engine;
1846 ti_new->class_desc = getLevelClassDescription(ti_new);
1849 pushTreeInfo(&node_parent->node_group, ti_new);
1852 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1853 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1855 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1856 TreeInfo *node_parent,
1857 char *level_directory,
1858 char *directory_name)
1860 char *directory_path = getPath2(level_directory, directory_name);
1861 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1862 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1863 LevelDirTree *leveldir_new = NULL;
1866 if (setup_file_hash == NULL)
1868 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1870 free(directory_path);
1876 leveldir_new = newTreeInfo();
1879 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1881 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1883 leveldir_new->filename = getStringCopy(directory_name);
1885 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1887 /* set all structure fields according to the token/value pairs */
1888 ldi = *leveldir_new;
1889 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1890 setSetupInfo(levelinfo_tokens, i,
1891 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1892 *leveldir_new = ldi;
1895 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1896 setString(&leveldir_new->name, leveldir_new->filename);
1898 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1900 free(leveldir_new->name);
1901 leveldir_new->name = getStringCopy(leveldir_new->filename);
1905 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1907 if (leveldir_new->identifier == NULL)
1908 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1910 if (leveldir_new->name_sorting == NULL)
1911 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1913 if (node_parent == NULL) /* top level group */
1915 leveldir_new->basepath = getStringCopy(level_directory);
1916 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1918 else /* sub level group */
1920 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1921 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1924 if (leveldir_new->levels < 1)
1925 leveldir_new->levels = 1;
1927 leveldir_new->last_level =
1928 leveldir_new->first_level + leveldir_new->levels - 1;
1931 leveldir_new->user_defined =
1932 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1934 leveldir_new->user_defined =
1935 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1938 leveldir_new->color = LEVELCOLOR(leveldir_new);
1940 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1942 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1945 leveldir_new->handicap_level = /* set handicap to default value */
1946 (leveldir_new->user_defined ?
1947 leveldir_new->last_level :
1948 leveldir_new->first_level);
1950 pushTreeInfo(node_first, leveldir_new);
1952 freeSetupFileHash(setup_file_hash);
1954 if (leveldir_new->level_group)
1956 /* create node to link back to current level directory */
1957 createParentTreeInfoNode(leveldir_new);
1959 /* step into sub-directory and look for more level series */
1960 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1961 leveldir_new, directory_path);
1964 free(directory_path);
1970 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1971 TreeInfo *node_parent,
1972 char *level_directory)
1975 struct dirent *dir_entry;
1976 boolean valid_entry_found = FALSE;
1978 if ((dir = opendir(level_directory)) == NULL)
1980 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1984 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1986 struct stat file_status;
1987 char *directory_name = dir_entry->d_name;
1988 char *directory_path = getPath2(level_directory, directory_name);
1990 /* skip entries for current and parent directory */
1991 if (strcmp(directory_name, ".") == 0 ||
1992 strcmp(directory_name, "..") == 0)
1994 free(directory_path);
1998 /* find out if directory entry is itself a directory */
1999 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2000 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2002 free(directory_path);
2006 free(directory_path);
2008 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2009 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2010 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2013 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2020 if (!valid_entry_found)
2022 /* check if this directory directly contains a file "levelinfo.conf" */
2023 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2024 level_directory, ".");
2027 if (!valid_entry_found)
2028 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2032 void LoadLevelInfo()
2034 InitUserLevelDirectory(getLoginName());
2036 DrawInitText("Loading level series:", 120, FC_GREEN);
2038 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2039 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2041 /* before sorting, the first entries will be from the user directory */
2042 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2044 if (leveldir_first == NULL)
2045 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2047 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2050 dumpTreeInfo(leveldir_first, 0);
2054 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2055 TreeInfo *node_parent,
2056 char *base_directory,
2057 char *directory_name, int type)
2059 char *directory_path = getPath2(base_directory, directory_name);
2060 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2061 SetupFileHash *setup_file_hash = NULL;
2062 TreeInfo *artwork_new = NULL;
2065 if (access(filename, F_OK) == 0) /* file exists */
2066 setup_file_hash = loadSetupFileHash(filename);
2068 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2071 struct dirent *dir_entry;
2072 boolean valid_file_found = FALSE;
2074 if ((dir = opendir(directory_path)) != NULL)
2076 while ((dir_entry = readdir(dir)) != NULL)
2078 char *entry_name = dir_entry->d_name;
2080 if (FileIsArtworkType(entry_name, type))
2082 valid_file_found = TRUE;
2090 if (!valid_file_found)
2092 if (strcmp(directory_name, ".") != 0)
2093 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2095 free(directory_path);
2102 artwork_new = newTreeInfo();
2105 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2107 setTreeInfoToDefaults(artwork_new, type);
2109 artwork_new->filename = getStringCopy(directory_name);
2111 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2114 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2117 /* set all structure fields according to the token/value pairs */
2119 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2120 setSetupInfo(levelinfo_tokens, i,
2121 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2125 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2126 setString(&artwork_new->name, artwork_new->filename);
2128 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2130 free(artwork_new->name);
2131 artwork_new->name = getStringCopy(artwork_new->filename);
2136 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2139 if (artwork_new->identifier == NULL)
2140 artwork_new->identifier = getStringCopy(artwork_new->filename);
2142 if (artwork_new->name_sorting == NULL)
2143 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2146 if (node_parent == NULL) /* top level group */
2148 artwork_new->basepath = getStringCopy(base_directory);
2149 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2151 else /* sub level group */
2153 artwork_new->basepath = getStringCopy(node_parent->basepath);
2154 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2158 artwork_new->user_defined =
2159 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2161 artwork_new->user_defined =
2162 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2165 /* (may use ".sort_priority" from "setup_file_hash" above) */
2166 artwork_new->color = ARTWORKCOLOR(artwork_new);
2168 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2170 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2173 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2176 if (artwork_new->name != NULL)
2178 free(artwork_new->name);
2179 artwork_new->name = NULL;
2184 if (artwork_new->identifier != NULL)
2186 free(artwork_new->identifier);
2187 artwork_new->identifier = NULL;
2191 if (strcmp(artwork_new->filename, ".") == 0)
2193 if (artwork_new->user_defined)
2196 setString(&artwork_new->identifier, "private");
2198 artwork_new->identifier = getStringCopy("private");
2200 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2205 setString(&artwork_new->identifier, "classic");
2207 artwork_new->identifier = getStringCopy("classic");
2209 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2212 /* set to new values after changing ".sort_priority" */
2213 artwork_new->color = ARTWORKCOLOR(artwork_new);
2215 setString(&artwork_new->class_desc,
2216 getLevelClassDescription(artwork_new));
2218 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2224 setString(&artwork_new->identifier, artwork_new->filename);
2226 artwork_new->identifier = getStringCopy(artwork_new->filename);
2231 setString(&artwork_new->name, artwork_new->identifier);
2232 setString(&artwork_new->name_sorting, artwork_new->name);
2234 artwork_new->name = getStringCopy(artwork_new->identifier);
2235 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2239 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2241 pushTreeInfo(node_first, artwork_new);
2243 freeSetupFileHash(setup_file_hash);
2245 free(directory_path);
2251 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2252 TreeInfo *node_parent,
2253 char *base_directory, int type)
2256 struct dirent *dir_entry;
2257 boolean valid_entry_found = FALSE;
2259 if ((dir = opendir(base_directory)) == NULL)
2261 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2262 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2266 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2268 struct stat file_status;
2269 char *directory_name = dir_entry->d_name;
2270 char *directory_path = getPath2(base_directory, directory_name);
2272 /* skip entries for current and parent directory */
2273 if (strcmp(directory_name, ".") == 0 ||
2274 strcmp(directory_name, "..") == 0)
2276 free(directory_path);
2280 /* find out if directory entry is itself a directory */
2281 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2282 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2284 free(directory_path);
2288 free(directory_path);
2290 /* check if this directory contains artwork with or without config file */
2291 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2293 directory_name, type);
2298 /* check if this directory directly contains artwork itself */
2299 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2300 base_directory, ".",
2302 if (!valid_entry_found)
2303 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2307 static TreeInfo *getDummyArtworkInfo(int type)
2309 /* this is only needed when there is completely no artwork available */
2310 TreeInfo *artwork_new = newTreeInfo();
2312 setTreeInfoToDefaults(artwork_new, type);
2315 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2316 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2317 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2319 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2320 setString(&artwork_new->name, UNDEFINED_FILENAME);
2321 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2323 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2324 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2325 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2327 if (artwork_new->name != NULL)
2328 free(artwork_new->name);
2330 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2331 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2332 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2338 void LoadArtworkInfo()
2340 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2342 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2343 options.graphics_directory,
2344 TREE_TYPE_GRAPHICS_DIR);
2345 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2346 getUserGraphicsDir(),
2347 TREE_TYPE_GRAPHICS_DIR);
2349 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2350 options.sounds_directory,
2351 TREE_TYPE_SOUNDS_DIR);
2352 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2354 TREE_TYPE_SOUNDS_DIR);
2356 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2357 options.music_directory,
2358 TREE_TYPE_MUSIC_DIR);
2359 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2361 TREE_TYPE_MUSIC_DIR);
2363 if (artwork.gfx_first == NULL)
2364 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2365 if (artwork.snd_first == NULL)
2366 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2367 if (artwork.mus_first == NULL)
2368 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2370 /* before sorting, the first entries will be from the user directory */
2371 artwork.gfx_current =
2372 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2373 if (artwork.gfx_current == NULL)
2374 artwork.gfx_current =
2375 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2376 if (artwork.gfx_current == NULL)
2377 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2379 artwork.snd_current =
2380 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2381 if (artwork.snd_current == NULL)
2382 artwork.snd_current =
2383 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2384 if (artwork.snd_current == NULL)
2385 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2387 artwork.mus_current =
2388 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2389 if (artwork.mus_current == NULL)
2390 artwork.mus_current =
2391 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2392 if (artwork.mus_current == NULL)
2393 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2395 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2396 artwork.snd_current_identifier = artwork.snd_current->identifier;
2397 artwork.mus_current_identifier = artwork.mus_current->identifier;
2400 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2401 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2402 printf("music set == %s\n\n", artwork.mus_current_identifier);
2405 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2406 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2407 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2410 dumpTreeInfo(artwork.gfx_first, 0);
2411 dumpTreeInfo(artwork.snd_first, 0);
2412 dumpTreeInfo(artwork.mus_first, 0);
2416 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2417 LevelDirTree *level_node)
2419 /* recursively check all level directories for artwork sub-directories */
2423 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2424 ARTWORK_DIRECTORY((*artwork_node)->type));
2427 if (!level_node->parent_link)
2428 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2429 level_node->filename, level_node->name);
2432 if (!level_node->parent_link)
2434 TreeInfo *topnode_last = *artwork_node;
2436 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2437 (*artwork_node)->type);
2439 if (topnode_last != *artwork_node)
2441 free((*artwork_node)->identifier);
2442 free((*artwork_node)->name);
2443 free((*artwork_node)->name_sorting);
2445 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2446 (*artwork_node)->name = getStringCopy(level_node->name);
2447 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2449 (*artwork_node)->sort_priority = level_node->sort_priority;
2450 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2456 if (level_node->node_group != NULL)
2457 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2459 level_node = level_node->next;
2463 void LoadLevelArtworkInfo()
2465 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2467 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2468 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2469 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2471 /* needed for reloading level artwork not known at ealier stage */
2473 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2475 artwork.gfx_current =
2476 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2477 if (artwork.gfx_current == NULL)
2478 artwork.gfx_current =
2479 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2480 if (artwork.gfx_current == NULL)
2481 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2484 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2486 artwork.snd_current =
2487 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2488 if (artwork.snd_current == NULL)
2489 artwork.snd_current =
2490 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2491 if (artwork.snd_current == NULL)
2492 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2495 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2497 artwork.mus_current =
2498 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2499 if (artwork.mus_current == NULL)
2500 artwork.mus_current =
2501 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2502 if (artwork.mus_current == NULL)
2503 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2506 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2507 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2508 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2511 dumpTreeInfo(artwork.gfx_first, 0);
2512 dumpTreeInfo(artwork.snd_first, 0);
2513 dumpTreeInfo(artwork.mus_first, 0);
2517 static void SaveUserLevelInfo()
2519 LevelDirTree *level_info;
2524 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2526 if (!(file = fopen(filename, MODE_WRITE)))
2528 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2533 level_info = newTreeInfo();
2535 /* always start with reliable default values */
2536 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2539 setString(&level_info->name, getLoginName());
2540 setString(&level_info->author, getRealName());
2541 level_info->levels = 100;
2542 level_info->first_level = 1;
2543 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2544 level_info->readonly = FALSE;
2545 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2546 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2547 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2549 ldi.name = getStringCopy(getLoginName());
2550 ldi.author = getStringCopy(getRealName());
2552 ldi.first_level = 1;
2553 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2554 ldi.readonly = FALSE;
2555 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2556 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2557 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2560 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2561 getCookie("LEVELINFO")));
2564 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2565 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2566 i != LEVELINFO_TOKEN_NAME_SORTING &&
2567 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2568 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2572 SetFilePermissions(filename, PERMS_PRIVATE);
2574 freeTreeInfo(level_info);
2578 char *getSetupValue(int type, void *value)
2580 static char value_string[MAX_LINE_LEN];
2588 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2592 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2596 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2600 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2604 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2608 sprintf(value_string, "%d", *(int *)value);
2612 strcpy(value_string, *(char **)value);
2616 value_string[0] = '\0';
2620 return value_string;
2623 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2627 static char token_string[MAX_LINE_LEN];
2628 int token_type = token_info[token_nr].type;
2629 void *setup_value = token_info[token_nr].value;
2630 char *token_text = token_info[token_nr].text;
2631 char *value_string = getSetupValue(token_type, setup_value);
2633 /* build complete token string */
2634 sprintf(token_string, "%s%s", prefix, token_text);
2636 /* build setup entry line */
2637 line = getFormattedSetupEntry(token_string, value_string);
2639 if (token_type == TYPE_KEY_X11)
2641 Key key = *(Key *)setup_value;
2642 char *keyname = getKeyNameFromKey(key);
2644 /* add comment, if useful */
2645 if (strcmp(keyname, "(undefined)") != 0 &&
2646 strcmp(keyname, "(unknown)") != 0)
2648 /* add at least one whitespace */
2650 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2654 strcat(line, keyname);
2661 void LoadLevelSetup_LastSeries()
2663 /* ----------------------------------------------------------------------- */
2664 /* ~/.<program>/levelsetup.conf */
2665 /* ----------------------------------------------------------------------- */
2667 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2668 SetupFileHash *level_setup_hash = NULL;
2670 /* always start with reliable default values */
2671 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2673 if ((level_setup_hash = loadSetupFileHash(filename)))
2675 char *last_level_series =
2676 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2678 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2680 if (leveldir_current == NULL)
2681 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2683 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2685 freeSetupFileHash(level_setup_hash);
2688 Error(ERR_WARN, "using default setup values");
2693 void SaveLevelSetup_LastSeries()
2695 /* ----------------------------------------------------------------------- */
2696 /* ~/.<program>/levelsetup.conf */
2697 /* ----------------------------------------------------------------------- */
2699 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2700 char *level_subdir = leveldir_current->filename;
2703 InitUserDataDirectory();
2705 if (!(file = fopen(filename, MODE_WRITE)))
2707 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2712 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2713 getCookie("LEVELSETUP")));
2714 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2719 SetFilePermissions(filename, PERMS_PRIVATE);
2724 static void checkSeriesInfo()
2726 static char *level_directory = NULL;
2728 struct dirent *dir_entry;
2730 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2732 level_directory = getPath2((leveldir_current->user_defined ?
2733 getUserLevelDir(NULL) :
2734 options.level_directory),
2735 leveldir_current->fullpath);
2737 if ((dir = opendir(level_directory)) == NULL)
2739 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2743 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2745 if (strlen(dir_entry->d_name) > 4 &&
2746 dir_entry->d_name[3] == '.' &&
2747 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2749 char levelnum_str[4];
2752 strncpy(levelnum_str, dir_entry->d_name, 3);
2753 levelnum_str[3] = '\0';
2755 levelnum_value = atoi(levelnum_str);
2758 if (levelnum_value < leveldir_current->first_level)
2760 Error(ERR_WARN, "additional level %d found", levelnum_value);
2761 leveldir_current->first_level = levelnum_value;
2763 else if (levelnum_value > leveldir_current->last_level)
2765 Error(ERR_WARN, "additional level %d found", levelnum_value);
2766 leveldir_current->last_level = levelnum_value;
2775 void LoadLevelSetup_SeriesInfo()
2778 SetupFileHash *level_setup_hash = NULL;
2779 char *level_subdir = leveldir_current->filename;
2781 /* always start with reliable default values */
2782 level_nr = leveldir_current->first_level;
2784 checkSeriesInfo(leveldir_current);
2786 /* ----------------------------------------------------------------------- */
2787 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2788 /* ----------------------------------------------------------------------- */
2790 level_subdir = leveldir_current->filename;
2792 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2794 if ((level_setup_hash = loadSetupFileHash(filename)))
2798 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2802 level_nr = atoi(token_value);
2804 if (level_nr < leveldir_current->first_level)
2805 level_nr = leveldir_current->first_level;
2806 if (level_nr > leveldir_current->last_level)
2807 level_nr = leveldir_current->last_level;
2810 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2814 int level_nr = atoi(token_value);
2816 if (level_nr < leveldir_current->first_level)
2817 level_nr = leveldir_current->first_level;
2818 if (level_nr > leveldir_current->last_level + 1)
2819 level_nr = leveldir_current->last_level;
2821 if (leveldir_current->user_defined)
2822 level_nr = leveldir_current->last_level;
2824 leveldir_current->handicap_level = level_nr;
2827 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2829 freeSetupFileHash(level_setup_hash);
2832 Error(ERR_WARN, "using default setup values");
2837 void SaveLevelSetup_SeriesInfo()
2840 char *level_subdir = leveldir_current->filename;
2841 char *level_nr_str = int2str(level_nr, 0);
2842 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2845 /* ----------------------------------------------------------------------- */
2846 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2847 /* ----------------------------------------------------------------------- */
2849 InitLevelSetupDirectory(level_subdir);
2851 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2853 if (!(file = fopen(filename, MODE_WRITE)))
2855 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2860 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2861 getCookie("LEVELSETUP")));
2862 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2864 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2865 handicap_level_str));
2869 SetFilePermissions(filename, PERMS_PRIVATE);