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);
1238 static void printSetupFileList(SetupFileList *list)
1243 printf("token: '%s'\n", list->token);
1244 printf("value: '%s'\n", list->value);
1246 printSetupFileList(list->next);
1251 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1252 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1253 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1254 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1256 #define insert_hash_entry hashtable_insert
1257 #define search_hash_entry hashtable_search
1258 #define change_hash_entry hashtable_change
1259 #define remove_hash_entry hashtable_remove
1262 static unsigned int get_hash_from_key(void *key)
1267 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1268 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1269 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1270 it works better than many other constants, prime or not) has never been
1271 adequately explained.
1273 If you just want to have a good hash function, and cannot wait, djb2
1274 is one of the best string hash functions i know. It has excellent
1275 distribution and speed on many different sets of keys and table sizes.
1276 You are not likely to do better with one of the "well known" functions
1277 such as PJW, K&R, etc.
1279 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1282 char *str = (char *)key;
1283 unsigned int hash = 5381;
1286 while ((c = *str++))
1287 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1292 static int keys_are_equal(void *key1, void *key2)
1294 return (strcmp((char *)key1, (char *)key2) == 0);
1297 SetupFileHash *newSetupFileHash()
1299 SetupFileHash *new_hash =
1300 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1302 if (new_hash == NULL)
1303 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1308 void freeSetupFileHash(SetupFileHash *hash)
1313 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1316 char *getHashEntry(SetupFileHash *hash, char *token)
1321 return search_hash_entry(hash, token);
1324 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1331 value_copy = getStringCopy(value);
1333 /* change value; if it does not exist, insert it as new */
1334 if (!change_hash_entry(hash, token, value_copy))
1335 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1336 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1341 static void printSetupFileHash(SetupFileHash *hash)
1343 BEGIN_HASH_ITERATION(hash, itr)
1345 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1346 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1348 END_HASH_ITERATION(hash, itr)
1353 static void *loadSetupFileData(char *filename, boolean use_hash)
1356 char line[MAX_LINE_LEN];
1357 char *token, *value, *line_ptr;
1358 void *setup_file_data, *insert_ptr = NULL;
1362 setup_file_data = newSetupFileHash();
1364 insert_ptr = setup_file_data = newSetupFileList("", "");
1366 if (!(file = fopen(filename, MODE_READ)))
1368 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1374 /* read next line of input file */
1375 if (!fgets(line, MAX_LINE_LEN, file))
1378 /* cut trailing comment or whitespace from input line */
1379 for (line_ptr = line; *line_ptr; line_ptr++)
1381 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1388 /* cut trailing whitespaces from input line */
1389 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1390 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1393 /* ignore empty lines */
1397 line_len = strlen(line);
1399 /* cut leading whitespaces from token */
1400 for (token = line; *token; token++)
1401 if (*token != ' ' && *token != '\t')
1404 /* find end of token */
1405 for (line_ptr = token; *line_ptr; line_ptr++)
1407 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1414 if (line_ptr < line + line_len)
1415 value = line_ptr + 1;
1418 value = "true"; /* treat tokens without value as "true" */
1423 /* cut leading whitespaces from value */
1424 for (; *value; value++)
1425 if (*value != ' ' && *value != '\t')
1428 if (*token && *value)
1431 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1433 insert_ptr = setListEntry((SetupFileList *)insert_ptr, token, value);
1441 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1442 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1446 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1447 SetupFileList *first_valid_list_entry = setup_file_list->next;
1449 /* free empty list header */
1450 setup_file_list->next = NULL;
1451 freeSetupFileList(setup_file_list);
1452 setup_file_data = first_valid_list_entry;
1454 if (first_valid_list_entry == NULL)
1455 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1458 return setup_file_data;
1461 SetupFileList *loadSetupFileList(char *filename)
1463 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1466 SetupFileHash *loadSetupFileHash(char *filename)
1468 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1471 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1474 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1477 Error(ERR_WARN, "configuration file has no file identifier");
1478 else if (!checkCookieString(value, identifier))
1479 Error(ERR_WARN, "configuration file has wrong file identifier");
1483 /* ========================================================================= */
1484 /* setup file stuff */
1485 /* ========================================================================= */
1487 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1488 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1489 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1491 /* level directory info */
1492 #define LEVELINFO_TOKEN_IDENTIFIER 0
1493 #define LEVELINFO_TOKEN_NAME 1
1494 #define LEVELINFO_TOKEN_NAME_SORTING 2
1495 #define LEVELINFO_TOKEN_AUTHOR 3
1496 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1497 #define LEVELINFO_TOKEN_LEVELS 5
1498 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1499 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1500 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1501 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1502 #define LEVELINFO_TOKEN_READONLY 10
1503 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1504 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1505 #define LEVELINFO_TOKEN_MUSIC_SET 13
1507 #define NUM_LEVELINFO_TOKENS 14
1509 static LevelDirTree ldi;
1511 static struct TokenInfo levelinfo_tokens[] =
1513 /* level directory info */
1514 { TYPE_STRING, &ldi.identifier, "identifier" },
1515 { TYPE_STRING, &ldi.name, "name" },
1516 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1517 { TYPE_STRING, &ldi.author, "author" },
1518 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1519 { TYPE_INTEGER, &ldi.levels, "levels" },
1520 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1521 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1522 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1523 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1524 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1525 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1526 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1527 { TYPE_STRING, &ldi.music_set, "music_set" }
1530 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1534 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1535 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1536 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1537 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1540 ldi->node_parent = NULL;
1541 ldi->node_group = NULL;
1545 ldi->cl_cursor = -1;
1547 ldi->filename = NULL;
1548 ldi->fullpath = NULL;
1549 ldi->basepath = NULL;
1550 ldi->identifier = NULL;
1551 ldi->name = getStringCopy(ANONYMOUS_NAME);
1552 ldi->name_sorting = NULL;
1553 ldi->author = getStringCopy(ANONYMOUS_NAME);
1555 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1556 ldi->latest_engine = FALSE; /* default: get from level */
1557 ldi->parent_link = FALSE;
1558 ldi->user_defined = FALSE;
1560 ldi->class_desc = NULL;
1562 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1564 ldi->imported_from = NULL;
1566 ldi->graphics_set = NULL;
1567 ldi->sounds_set = NULL;
1568 ldi->music_set = NULL;
1569 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1570 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1571 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1574 ldi->first_level = 0;
1575 ldi->last_level = 0;
1576 ldi->level_group = FALSE;
1577 ldi->handicap_level = 0;
1578 ldi->readonly = TRUE;
1582 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1586 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1588 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1594 /* copy all values from the parent structure */
1596 ldi->type = parent->type;
1598 ldi->node_top = parent->node_top;
1599 ldi->node_parent = parent;
1600 ldi->node_group = NULL;
1604 ldi->cl_cursor = -1;
1606 ldi->filename = NULL;
1607 ldi->fullpath = NULL;
1608 ldi->basepath = NULL;
1609 ldi->identifier = NULL;
1610 ldi->name = getStringCopy(ANONYMOUS_NAME);
1611 ldi->name_sorting = NULL;
1612 ldi->author = getStringCopy(parent->author);
1614 ldi->sort_priority = parent->sort_priority;
1615 ldi->latest_engine = parent->latest_engine;
1616 ldi->parent_link = FALSE;
1617 ldi->user_defined = parent->user_defined;
1618 ldi->color = parent->color;
1619 ldi->class_desc = getStringCopy(parent->class_desc);
1621 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1623 ldi->imported_from = getStringCopy(parent->imported_from);
1625 ldi->graphics_set = NULL;
1626 ldi->sounds_set = NULL;
1627 ldi->music_set = NULL;
1628 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1629 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1630 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1633 ldi->first_level = 0;
1634 ldi->last_level = 0;
1635 ldi->level_group = FALSE;
1636 ldi->handicap_level = 0;
1637 ldi->readonly = TRUE;
1643 /* first copy all values from the parent structure ... */
1646 /* ... then set all fields to default that cannot be inherited from parent.
1647 This is especially important for all those fields that can be set from
1648 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1649 calls 'free()' for all already set token values which requires that no
1650 other structure's pointer may point to them!
1653 ldi->filename = NULL;
1654 ldi->fullpath = NULL;
1655 ldi->basepath = NULL;
1656 ldi->identifier = NULL;
1657 ldi->name = getStringCopy(ANONYMOUS_NAME);
1658 ldi->name_sorting = NULL;
1659 ldi->author = getStringCopy(parent->author);
1661 ldi->imported_from = getStringCopy(parent->imported_from);
1662 ldi->class_desc = getStringCopy(parent->class_desc);
1664 ldi->graphics_set = NULL;
1665 ldi->sounds_set = NULL;
1666 ldi->music_set = NULL;
1667 ldi->graphics_path = NULL;
1668 ldi->sounds_path = NULL;
1669 ldi->music_path = NULL;
1671 ldi->level_group = FALSE;
1672 ldi->parent_link = FALSE;
1674 ldi->node_top = parent->node_top;
1675 ldi->node_parent = parent;
1676 ldi->node_group = NULL;
1682 static void freeTreeInfo(TreeInfo *ldi)
1685 free(ldi->filename);
1687 free(ldi->fullpath);
1689 free(ldi->basepath);
1690 if (ldi->identifier)
1691 free(ldi->identifier);
1695 if (ldi->name_sorting)
1696 free(ldi->name_sorting);
1700 if (ldi->class_desc)
1701 free(ldi->class_desc);
1703 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1705 if (ldi->graphics_set)
1706 free(ldi->graphics_set);
1707 if (ldi->sounds_set)
1708 free(ldi->sounds_set);
1710 free(ldi->music_set);
1712 if (ldi->graphics_path)
1713 free(ldi->graphics_path);
1714 if (ldi->sounds_path)
1715 free(ldi->sounds_path);
1716 if (ldi->music_path)
1717 free(ldi->music_path);
1721 void setSetupInfo(struct TokenInfo *token_info,
1722 int token_nr, char *token_value)
1724 int token_type = token_info[token_nr].type;
1725 void *setup_value = token_info[token_nr].value;
1727 if (token_value == NULL)
1730 /* set setup field to corresponding token value */
1735 *(boolean *)setup_value = get_boolean_from_string(token_value);
1739 *(Key *)setup_value = getKeyFromKeyName(token_value);
1743 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1747 *(int *)setup_value = get_integer_from_string(token_value);
1751 if (*(char **)setup_value != NULL)
1752 free(*(char **)setup_value);
1753 *(char **)setup_value = getStringCopy(token_value);
1761 static int compareTreeInfoEntries(const void *object1, const void *object2)
1763 const TreeInfo *entry1 = *((TreeInfo **)object1);
1764 const TreeInfo *entry2 = *((TreeInfo **)object2);
1765 int class_sorting1, class_sorting2;
1768 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1770 class_sorting1 = LEVELSORTING(entry1);
1771 class_sorting2 = LEVELSORTING(entry2);
1775 class_sorting1 = ARTWORKSORTING(entry1);
1776 class_sorting2 = ARTWORKSORTING(entry2);
1779 if (entry1->parent_link || entry2->parent_link)
1780 compare_result = (entry1->parent_link ? -1 : +1);
1781 else if (entry1->sort_priority == entry2->sort_priority)
1783 char *name1 = getStringToLower(entry1->name_sorting);
1784 char *name2 = getStringToLower(entry2->name_sorting);
1786 compare_result = strcmp(name1, name2);
1791 else if (class_sorting1 == class_sorting2)
1792 compare_result = entry1->sort_priority - entry2->sort_priority;
1794 compare_result = class_sorting1 - class_sorting2;
1796 return compare_result;
1799 static void createParentTreeInfoNode(TreeInfo *node_parent)
1803 if (node_parent == NULL)
1806 ti_new = newTreeInfo();
1807 setTreeInfoToDefaults(ti_new, node_parent->type);
1809 ti_new->node_parent = node_parent;
1810 ti_new->parent_link = TRUE;
1813 setString(&ti_new->identifier, node_parent->identifier);
1814 setString(&ti_new->name, ".. (parent directory)");
1815 setString(&ti_new->name_sorting, ti_new->name);
1817 setString(&ti_new->filename, "..");
1818 setString(&ti_new->fullpath, node_parent->fullpath);
1820 ti_new->sort_priority = node_parent->sort_priority;
1821 ti_new->latest_engine = node_parent->latest_engine;
1823 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1825 ti_new->identifier = getStringCopy(node_parent->identifier);
1826 ti_new->name = ".. (parent directory)";
1827 ti_new->name_sorting = getStringCopy(ti_new->name);
1829 ti_new->filename = "..";
1830 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1832 ti_new->sort_priority = node_parent->sort_priority;
1833 ti_new->latest_engine = node_parent->latest_engine;
1835 ti_new->class_desc = getLevelClassDescription(ti_new);
1838 pushTreeInfo(&node_parent->node_group, ti_new);
1841 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1842 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1844 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1845 TreeInfo *node_parent,
1846 char *level_directory,
1847 char *directory_name)
1849 char *directory_path = getPath2(level_directory, directory_name);
1850 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1851 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1852 LevelDirTree *leveldir_new = NULL;
1855 if (setup_file_hash == NULL)
1857 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1859 free(directory_path);
1865 leveldir_new = newTreeInfo();
1868 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1870 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1872 leveldir_new->filename = getStringCopy(directory_name);
1874 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1876 /* set all structure fields according to the token/value pairs */
1877 ldi = *leveldir_new;
1878 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1879 setSetupInfo(levelinfo_tokens, i,
1880 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1881 *leveldir_new = ldi;
1884 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1885 setString(&leveldir_new->name, leveldir_new->filename);
1887 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1889 free(leveldir_new->name);
1890 leveldir_new->name = getStringCopy(leveldir_new->filename);
1894 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1896 if (leveldir_new->identifier == NULL)
1897 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1899 if (leveldir_new->name_sorting == NULL)
1900 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1902 if (node_parent == NULL) /* top level group */
1904 leveldir_new->basepath = getStringCopy(level_directory);
1905 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1907 else /* sub level group */
1909 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1910 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1913 if (leveldir_new->levels < 1)
1914 leveldir_new->levels = 1;
1916 leveldir_new->last_level =
1917 leveldir_new->first_level + leveldir_new->levels - 1;
1920 leveldir_new->user_defined =
1921 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1923 leveldir_new->user_defined =
1924 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1927 leveldir_new->color = LEVELCOLOR(leveldir_new);
1929 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1931 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1934 leveldir_new->handicap_level = /* set handicap to default value */
1935 (leveldir_new->user_defined ?
1936 leveldir_new->last_level :
1937 leveldir_new->first_level);
1939 pushTreeInfo(node_first, leveldir_new);
1941 freeSetupFileHash(setup_file_hash);
1943 if (leveldir_new->level_group)
1945 /* create node to link back to current level directory */
1946 createParentTreeInfoNode(leveldir_new);
1948 /* step into sub-directory and look for more level series */
1949 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1950 leveldir_new, directory_path);
1953 free(directory_path);
1959 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1960 TreeInfo *node_parent,
1961 char *level_directory)
1964 struct dirent *dir_entry;
1965 boolean valid_entry_found = FALSE;
1967 if ((dir = opendir(level_directory)) == NULL)
1969 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1973 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1975 struct stat file_status;
1976 char *directory_name = dir_entry->d_name;
1977 char *directory_path = getPath2(level_directory, directory_name);
1979 /* skip entries for current and parent directory */
1980 if (strcmp(directory_name, ".") == 0 ||
1981 strcmp(directory_name, "..") == 0)
1983 free(directory_path);
1987 /* find out if directory entry is itself a directory */
1988 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1989 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1991 free(directory_path);
1995 free(directory_path);
1997 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1998 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1999 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2002 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2009 if (!valid_entry_found)
2011 /* check if this directory directly contains a file "levelinfo.conf" */
2012 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2013 level_directory, ".");
2016 if (!valid_entry_found)
2017 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2021 void LoadLevelInfo()
2023 InitUserLevelDirectory(getLoginName());
2025 DrawInitText("Loading level series:", 120, FC_GREEN);
2027 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2028 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2030 /* before sorting, the first entries will be from the user directory */
2031 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2033 if (leveldir_first == NULL)
2034 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2036 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2039 dumpTreeInfo(leveldir_first, 0);
2043 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2044 TreeInfo *node_parent,
2045 char *base_directory,
2046 char *directory_name, int type)
2048 char *directory_path = getPath2(base_directory, directory_name);
2049 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2050 SetupFileHash *setup_file_hash = NULL;
2051 TreeInfo *artwork_new = NULL;
2054 if (access(filename, F_OK) == 0) /* file exists */
2055 setup_file_hash = loadSetupFileHash(filename);
2057 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2060 struct dirent *dir_entry;
2061 boolean valid_file_found = FALSE;
2063 if ((dir = opendir(directory_path)) != NULL)
2065 while ((dir_entry = readdir(dir)) != NULL)
2067 char *entry_name = dir_entry->d_name;
2069 if (FileIsArtworkType(entry_name, type))
2071 valid_file_found = TRUE;
2079 if (!valid_file_found)
2081 if (strcmp(directory_name, ".") != 0)
2082 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2084 free(directory_path);
2091 artwork_new = newTreeInfo();
2094 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2096 setTreeInfoToDefaults(artwork_new, type);
2098 artwork_new->filename = getStringCopy(directory_name);
2100 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2103 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2106 /* set all structure fields according to the token/value pairs */
2108 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2109 setSetupInfo(levelinfo_tokens, i,
2110 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2114 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2115 setString(&artwork_new->name, artwork_new->filename);
2117 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2119 free(artwork_new->name);
2120 artwork_new->name = getStringCopy(artwork_new->filename);
2125 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2128 if (artwork_new->identifier == NULL)
2129 artwork_new->identifier = getStringCopy(artwork_new->filename);
2131 if (artwork_new->name_sorting == NULL)
2132 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2135 if (node_parent == NULL) /* top level group */
2137 artwork_new->basepath = getStringCopy(base_directory);
2138 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2140 else /* sub level group */
2142 artwork_new->basepath = getStringCopy(node_parent->basepath);
2143 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2147 artwork_new->user_defined =
2148 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2150 artwork_new->user_defined =
2151 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2154 /* (may use ".sort_priority" from "setup_file_hash" above) */
2155 artwork_new->color = ARTWORKCOLOR(artwork_new);
2157 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2159 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2162 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2165 if (artwork_new->name != NULL)
2167 free(artwork_new->name);
2168 artwork_new->name = NULL;
2173 if (artwork_new->identifier != NULL)
2175 free(artwork_new->identifier);
2176 artwork_new->identifier = NULL;
2180 if (strcmp(artwork_new->filename, ".") == 0)
2182 if (artwork_new->user_defined)
2185 setString(&artwork_new->identifier, "private");
2187 artwork_new->identifier = getStringCopy("private");
2189 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2194 setString(&artwork_new->identifier, "classic");
2196 artwork_new->identifier = getStringCopy("classic");
2198 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2201 /* set to new values after changing ".sort_priority" */
2202 artwork_new->color = ARTWORKCOLOR(artwork_new);
2204 setString(&artwork_new->class_desc,
2205 getLevelClassDescription(artwork_new));
2207 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2213 setString(&artwork_new->identifier, artwork_new->filename);
2215 artwork_new->identifier = getStringCopy(artwork_new->filename);
2220 setString(&artwork_new->name, artwork_new->identifier);
2221 setString(&artwork_new->name_sorting, artwork_new->name);
2223 artwork_new->name = getStringCopy(artwork_new->identifier);
2224 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2228 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2230 pushTreeInfo(node_first, artwork_new);
2232 freeSetupFileHash(setup_file_hash);
2234 free(directory_path);
2240 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2241 TreeInfo *node_parent,
2242 char *base_directory, int type)
2245 struct dirent *dir_entry;
2246 boolean valid_entry_found = FALSE;
2248 if ((dir = opendir(base_directory)) == NULL)
2250 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2251 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2255 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2257 struct stat file_status;
2258 char *directory_name = dir_entry->d_name;
2259 char *directory_path = getPath2(base_directory, directory_name);
2261 /* skip entries for current and parent directory */
2262 if (strcmp(directory_name, ".") == 0 ||
2263 strcmp(directory_name, "..") == 0)
2265 free(directory_path);
2269 /* find out if directory entry is itself a directory */
2270 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2271 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2273 free(directory_path);
2277 free(directory_path);
2279 /* check if this directory contains artwork with or without config file */
2280 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2282 directory_name, type);
2287 /* check if this directory directly contains artwork itself */
2288 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2289 base_directory, ".",
2291 if (!valid_entry_found)
2292 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2296 static TreeInfo *getDummyArtworkInfo(int type)
2298 /* this is only needed when there is completely no artwork available */
2299 TreeInfo *artwork_new = newTreeInfo();
2301 setTreeInfoToDefaults(artwork_new, type);
2304 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2305 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2306 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2308 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2309 setString(&artwork_new->name, UNDEFINED_FILENAME);
2310 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2312 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2313 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2314 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2316 if (artwork_new->name != NULL)
2317 free(artwork_new->name);
2319 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2320 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2321 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2327 void LoadArtworkInfo()
2329 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2331 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2332 options.graphics_directory,
2333 TREE_TYPE_GRAPHICS_DIR);
2334 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2335 getUserGraphicsDir(),
2336 TREE_TYPE_GRAPHICS_DIR);
2338 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2339 options.sounds_directory,
2340 TREE_TYPE_SOUNDS_DIR);
2341 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2343 TREE_TYPE_SOUNDS_DIR);
2345 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2346 options.music_directory,
2347 TREE_TYPE_MUSIC_DIR);
2348 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2350 TREE_TYPE_MUSIC_DIR);
2352 if (artwork.gfx_first == NULL)
2353 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2354 if (artwork.snd_first == NULL)
2355 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2356 if (artwork.mus_first == NULL)
2357 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2359 /* before sorting, the first entries will be from the user directory */
2360 artwork.gfx_current =
2361 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2362 if (artwork.gfx_current == NULL)
2363 artwork.gfx_current =
2364 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2365 if (artwork.gfx_current == NULL)
2366 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2368 artwork.snd_current =
2369 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2370 if (artwork.snd_current == NULL)
2371 artwork.snd_current =
2372 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2373 if (artwork.snd_current == NULL)
2374 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2376 artwork.mus_current =
2377 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2378 if (artwork.mus_current == NULL)
2379 artwork.mus_current =
2380 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2381 if (artwork.mus_current == NULL)
2382 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2384 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2385 artwork.snd_current_identifier = artwork.snd_current->identifier;
2386 artwork.mus_current_identifier = artwork.mus_current->identifier;
2389 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2390 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2391 printf("music set == %s\n\n", artwork.mus_current_identifier);
2394 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2395 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2396 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2399 dumpTreeInfo(artwork.gfx_first, 0);
2400 dumpTreeInfo(artwork.snd_first, 0);
2401 dumpTreeInfo(artwork.mus_first, 0);
2405 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2406 LevelDirTree *level_node)
2408 /* recursively check all level directories for artwork sub-directories */
2412 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2413 ARTWORK_DIRECTORY((*artwork_node)->type));
2416 if (!level_node->parent_link)
2417 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2418 level_node->filename, level_node->name);
2421 if (!level_node->parent_link)
2423 TreeInfo *topnode_last = *artwork_node;
2425 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2426 (*artwork_node)->type);
2428 if (topnode_last != *artwork_node)
2430 free((*artwork_node)->identifier);
2431 free((*artwork_node)->name);
2432 free((*artwork_node)->name_sorting);
2434 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2435 (*artwork_node)->name = getStringCopy(level_node->name);
2436 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2438 (*artwork_node)->sort_priority = level_node->sort_priority;
2439 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2445 if (level_node->node_group != NULL)
2446 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2448 level_node = level_node->next;
2452 void LoadLevelArtworkInfo()
2454 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2456 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2457 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2458 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2460 /* needed for reloading level artwork not known at ealier stage */
2462 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2464 artwork.gfx_current =
2465 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2466 if (artwork.gfx_current == NULL)
2467 artwork.gfx_current =
2468 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2469 if (artwork.gfx_current == NULL)
2470 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2473 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2475 artwork.snd_current =
2476 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2477 if (artwork.snd_current == NULL)
2478 artwork.snd_current =
2479 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2480 if (artwork.snd_current == NULL)
2481 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2484 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2486 artwork.mus_current =
2487 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2488 if (artwork.mus_current == NULL)
2489 artwork.mus_current =
2490 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2491 if (artwork.mus_current == NULL)
2492 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2495 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2496 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2497 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2500 dumpTreeInfo(artwork.gfx_first, 0);
2501 dumpTreeInfo(artwork.snd_first, 0);
2502 dumpTreeInfo(artwork.mus_first, 0);
2506 static void SaveUserLevelInfo()
2508 LevelDirTree *level_info;
2513 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2515 if (!(file = fopen(filename, MODE_WRITE)))
2517 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2522 level_info = newTreeInfo();
2524 /* always start with reliable default values */
2525 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2528 setString(&level_info->name, getLoginName());
2529 setString(&level_info->author, getRealName());
2530 level_info->levels = 100;
2531 level_info->first_level = 1;
2532 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2533 level_info->readonly = FALSE;
2534 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2535 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2536 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2538 ldi.name = getStringCopy(getLoginName());
2539 ldi.author = getStringCopy(getRealName());
2541 ldi.first_level = 1;
2542 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2543 ldi.readonly = FALSE;
2544 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2545 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2546 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2549 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2550 getCookie("LEVELINFO")));
2553 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2554 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2555 i != LEVELINFO_TOKEN_NAME_SORTING &&
2556 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2557 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2561 SetFilePermissions(filename, PERMS_PRIVATE);
2563 freeTreeInfo(level_info);
2567 char *getSetupValue(int type, void *value)
2569 static char value_string[MAX_LINE_LEN];
2577 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2581 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2585 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2589 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2593 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2597 sprintf(value_string, "%d", *(int *)value);
2601 strcpy(value_string, *(char **)value);
2605 value_string[0] = '\0';
2609 return value_string;
2612 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2616 static char token_string[MAX_LINE_LEN];
2617 int token_type = token_info[token_nr].type;
2618 void *setup_value = token_info[token_nr].value;
2619 char *token_text = token_info[token_nr].text;
2620 char *value_string = getSetupValue(token_type, setup_value);
2622 /* build complete token string */
2623 sprintf(token_string, "%s%s", prefix, token_text);
2625 /* build setup entry line */
2626 line = getFormattedSetupEntry(token_string, value_string);
2628 if (token_type == TYPE_KEY_X11)
2630 Key key = *(Key *)setup_value;
2631 char *keyname = getKeyNameFromKey(key);
2633 /* add comment, if useful */
2634 if (strcmp(keyname, "(undefined)") != 0 &&
2635 strcmp(keyname, "(unknown)") != 0)
2637 /* add at least one whitespace */
2639 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2643 strcat(line, keyname);
2650 void LoadLevelSetup_LastSeries()
2652 /* ----------------------------------------------------------------------- */
2653 /* ~/.<program>/levelsetup.conf */
2654 /* ----------------------------------------------------------------------- */
2656 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2657 SetupFileHash *level_setup_hash = NULL;
2659 /* always start with reliable default values */
2660 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2662 if ((level_setup_hash = loadSetupFileHash(filename)))
2664 char *last_level_series =
2665 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2667 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2669 if (leveldir_current == NULL)
2670 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2672 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2674 freeSetupFileHash(level_setup_hash);
2677 Error(ERR_WARN, "using default setup values");
2682 void SaveLevelSetup_LastSeries()
2684 /* ----------------------------------------------------------------------- */
2685 /* ~/.<program>/levelsetup.conf */
2686 /* ----------------------------------------------------------------------- */
2688 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2689 char *level_subdir = leveldir_current->filename;
2692 InitUserDataDirectory();
2694 if (!(file = fopen(filename, MODE_WRITE)))
2696 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2701 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2702 getCookie("LEVELSETUP")));
2703 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2708 SetFilePermissions(filename, PERMS_PRIVATE);
2713 static void checkSeriesInfo()
2715 static char *level_directory = NULL;
2717 struct dirent *dir_entry;
2719 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2721 level_directory = getPath2((leveldir_current->user_defined ?
2722 getUserLevelDir(NULL) :
2723 options.level_directory),
2724 leveldir_current->fullpath);
2726 if ((dir = opendir(level_directory)) == NULL)
2728 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2732 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2734 if (strlen(dir_entry->d_name) > 4 &&
2735 dir_entry->d_name[3] == '.' &&
2736 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2738 char levelnum_str[4];
2741 strncpy(levelnum_str, dir_entry->d_name, 3);
2742 levelnum_str[3] = '\0';
2744 levelnum_value = atoi(levelnum_str);
2747 if (levelnum_value < leveldir_current->first_level)
2749 Error(ERR_WARN, "additional level %d found", levelnum_value);
2750 leveldir_current->first_level = levelnum_value;
2752 else if (levelnum_value > leveldir_current->last_level)
2754 Error(ERR_WARN, "additional level %d found", levelnum_value);
2755 leveldir_current->last_level = levelnum_value;
2764 void LoadLevelSetup_SeriesInfo()
2767 SetupFileHash *level_setup_hash = NULL;
2768 char *level_subdir = leveldir_current->filename;
2770 /* always start with reliable default values */
2771 level_nr = leveldir_current->first_level;
2773 checkSeriesInfo(leveldir_current);
2775 /* ----------------------------------------------------------------------- */
2776 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2777 /* ----------------------------------------------------------------------- */
2779 level_subdir = leveldir_current->filename;
2781 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2783 if ((level_setup_hash = loadSetupFileHash(filename)))
2787 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2791 level_nr = atoi(token_value);
2793 if (level_nr < leveldir_current->first_level)
2794 level_nr = leveldir_current->first_level;
2795 if (level_nr > leveldir_current->last_level)
2796 level_nr = leveldir_current->last_level;
2799 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2803 int level_nr = atoi(token_value);
2805 if (level_nr < leveldir_current->first_level)
2806 level_nr = leveldir_current->first_level;
2807 if (level_nr > leveldir_current->last_level + 1)
2808 level_nr = leveldir_current->last_level;
2810 if (leveldir_current->user_defined)
2811 level_nr = leveldir_current->last_level;
2813 leveldir_current->handicap_level = level_nr;
2816 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2818 freeSetupFileHash(level_setup_hash);
2821 Error(ERR_WARN, "using default setup values");
2826 void SaveLevelSetup_SeriesInfo()
2829 char *level_subdir = leveldir_current->filename;
2830 char *level_nr_str = int2str(level_nr, 0);
2831 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2834 /* ----------------------------------------------------------------------- */
2835 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2836 /* ----------------------------------------------------------------------- */
2838 InitLevelSetupDirectory(level_subdir);
2840 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2842 if (!(file = fopen(filename, MODE_WRITE)))
2844 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2849 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2850 getCookie("LEVELSETUP")));
2851 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2853 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2854 handicap_level_str));
2858 SetFilePermissions(filename, PERMS_PRIVATE);