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 *getHelpAnimFilename()
422 static char *filename = NULL;
424 if (filename != NULL)
427 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
432 char *getHelpTextFilename()
434 static char *filename = NULL;
436 if (filename != NULL)
439 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
444 char *getLevelSetInfoFilename()
446 static char *filename = NULL;
459 for (i = 0; basenames[i] != NULL; i++)
461 if (filename != NULL)
464 filename = getPath2(getCurrentLevelDir(), basenames[i]);
465 if (fileExists(filename))
472 static char *getCorrectedArtworkBasename(char *basename)
474 char *basename_corrected = basename;
476 #if defined(PLATFORM_MSDOS)
477 if (program.filename_prefix != NULL)
479 int prefix_len = strlen(program.filename_prefix);
481 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
482 basename_corrected = &basename[prefix_len];
484 /* if corrected filename is still longer than standard MS-DOS filename
485 size (8 characters + 1 dot + 3 characters file extension), shorten
486 filename by writing file extension after 8th basename character */
487 if (strlen(basename_corrected) > 8 + 1 + 3)
489 static char *msdos_filename = NULL;
491 if (msdos_filename != NULL)
492 free(msdos_filename);
494 msdos_filename = getStringCopy(basename_corrected);
495 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
497 basename_corrected = msdos_filename;
502 return basename_corrected;
505 char *getCustomImageFilename(char *basename)
507 static char *filename = NULL;
508 boolean skip_setup_artwork = FALSE;
510 if (filename != NULL)
513 basename = getCorrectedArtworkBasename(basename);
515 if (!setup.override_level_graphics)
517 /* 1st try: look for special artwork in current level series directory */
518 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
519 if (fileExists(filename))
524 /* check if there is special artwork configured in level series config */
525 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
527 /* 2nd try: look for special artwork configured in level series config */
528 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
529 if (fileExists(filename))
534 /* take missing artwork configured in level set config from default */
535 skip_setup_artwork = TRUE;
539 if (!skip_setup_artwork)
541 /* 3rd try: look for special artwork in configured artwork directory */
542 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
543 if (fileExists(filename))
549 /* 4th try: look for default artwork in new default artwork directory */
550 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
551 if (fileExists(filename))
556 /* 5th try: look for default artwork in old default artwork directory */
557 filename = getPath2(options.graphics_directory, basename);
558 if (fileExists(filename))
561 return NULL; /* cannot find specified artwork file anywhere */
564 char *getCustomSoundFilename(char *basename)
566 static char *filename = NULL;
567 boolean skip_setup_artwork = FALSE;
569 if (filename != NULL)
572 basename = getCorrectedArtworkBasename(basename);
574 if (!setup.override_level_sounds)
576 /* 1st try: look for special artwork in current level series directory */
577 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
578 if (fileExists(filename))
583 /* check if there is special artwork configured in level series config */
584 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
586 /* 2nd try: look for special artwork configured in level series config */
587 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
588 if (fileExists(filename))
593 /* take missing artwork configured in level set config from default */
594 skip_setup_artwork = TRUE;
598 if (!skip_setup_artwork)
600 /* 3rd try: look for special artwork in configured artwork directory */
601 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
602 if (fileExists(filename))
608 /* 4th try: look for default artwork in new default artwork directory */
609 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
610 if (fileExists(filename))
615 /* 5th try: look for default artwork in old default artwork directory */
616 filename = getPath2(options.sounds_directory, basename);
617 if (fileExists(filename))
620 return NULL; /* cannot find specified artwork file anywhere */
623 char *getCustomMusicFilename(char *basename)
625 static char *filename = NULL;
626 boolean skip_setup_artwork = FALSE;
628 if (filename != NULL)
631 basename = getCorrectedArtworkBasename(basename);
633 if (!setup.override_level_music)
635 /* 1st try: look for special artwork in current level series directory */
636 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
637 if (fileExists(filename))
642 /* check if there is special artwork configured in level series config */
643 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
645 /* 2nd try: look for special artwork configured in level series config */
646 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
647 if (fileExists(filename))
652 /* take missing artwork configured in level set config from default */
653 skip_setup_artwork = TRUE;
657 if (!skip_setup_artwork)
659 /* 3rd try: look for special artwork in configured artwork directory */
660 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
661 if (fileExists(filename))
667 /* 4th try: look for default artwork in new default artwork directory */
668 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
669 if (fileExists(filename))
674 /* 5th try: look for default artwork in old default artwork directory */
675 filename = getPath2(options.music_directory, basename);
676 if (fileExists(filename))
679 return NULL; /* cannot find specified artwork file anywhere */
682 char *getCustomArtworkFilename(char *basename, int type)
684 if (type == ARTWORK_TYPE_GRAPHICS)
685 return getCustomImageFilename(basename);
686 else if (type == ARTWORK_TYPE_SOUNDS)
687 return getCustomSoundFilename(basename);
688 else if (type == ARTWORK_TYPE_MUSIC)
689 return getCustomMusicFilename(basename);
691 return UNDEFINED_FILENAME;
694 char *getCustomArtworkConfigFilename(int type)
696 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
699 char *getCustomArtworkLevelConfigFilename(int type)
701 static char *filename = NULL;
703 if (filename != NULL)
706 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
711 char *getCustomMusicDirectory(void)
713 static char *directory = NULL;
714 boolean skip_setup_artwork = FALSE;
716 if (directory != NULL)
719 if (!setup.override_level_music)
721 /* 1st try: look for special artwork in current level series directory */
722 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
723 if (fileExists(directory))
728 /* check if there is special artwork configured in level series config */
729 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
731 /* 2nd try: look for special artwork configured in level series config */
732 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
733 if (fileExists(directory))
738 /* take missing artwork configured in level set config from default */
739 skip_setup_artwork = TRUE;
743 if (!skip_setup_artwork)
745 /* 3rd try: look for special artwork in configured artwork directory */
746 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
747 if (fileExists(directory))
753 /* 4th try: look for default artwork in new default artwork directory */
754 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
755 if (fileExists(directory))
760 /* 5th try: look for default artwork in old default artwork directory */
761 directory = getStringCopy(options.music_directory);
762 if (fileExists(directory))
765 return NULL; /* cannot find specified artwork file anywhere */
768 void InitTapeDirectory(char *level_subdir)
770 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
771 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
772 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
775 void InitScoreDirectory(char *level_subdir)
777 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
778 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
779 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
782 static void SaveUserLevelInfo();
784 void InitUserLevelDirectory(char *level_subdir)
786 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
788 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
789 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
790 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
796 void InitLevelSetupDirectory(char *level_subdir)
798 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
799 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
800 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
804 /* ------------------------------------------------------------------------- */
805 /* some functions to handle lists of level directories */
806 /* ------------------------------------------------------------------------- */
808 TreeInfo *newTreeInfo()
810 return checked_calloc(sizeof(TreeInfo));
813 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
815 node_new->next = *node_first;
816 *node_first = node_new;
819 int numTreeInfo(TreeInfo *node)
832 boolean validLevelSeries(TreeInfo *node)
834 return (node != NULL && !node->node_group && !node->parent_link);
837 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
842 if (node->node_group) /* enter level group (step down into tree) */
843 return getFirstValidTreeInfoEntry(node->node_group);
844 else if (node->parent_link) /* skip start entry of level group */
846 if (node->next) /* get first real level series entry */
847 return getFirstValidTreeInfoEntry(node->next);
848 else /* leave empty level group and go on */
849 return getFirstValidTreeInfoEntry(node->node_parent->next);
851 else /* this seems to be a regular level series */
855 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
860 if (node->node_parent == NULL) /* top level group */
861 return *node->node_top;
862 else /* sub level group */
863 return node->node_parent->node_group;
866 int numTreeInfoInGroup(TreeInfo *node)
868 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
871 int posTreeInfo(TreeInfo *node)
873 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
878 if (node_cmp == node)
882 node_cmp = node_cmp->next;
888 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
890 TreeInfo *node_default = node;
905 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
907 if (identifier == NULL)
912 if (node->node_group)
914 TreeInfo *node_group;
916 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
921 else if (!node->parent_link)
923 if (strcmp(identifier, node->identifier) == 0)
933 void dumpTreeInfo(TreeInfo *node, int depth)
937 printf("Dumping TreeInfo:\n");
941 for (i = 0; i < (depth + 1) * 3; i++)
945 printf("filename == '%s' ['%s', '%s'] [%d])\n",
946 node->filename, node->fullpath, node->basepath, node->user_defined);
948 printf("filename == '%s' (%s) [%s] (%d)\n",
949 node->filename, node->name, node->identifier, node->sort_priority);
952 if (node->node_group != NULL)
953 dumpTreeInfo(node->node_group, depth + 1);
959 void sortTreeInfo(TreeInfo **node_first,
960 int (*compare_function)(const void *, const void *))
962 int num_nodes = numTreeInfo(*node_first);
963 TreeInfo **sort_array;
964 TreeInfo *node = *node_first;
970 /* allocate array for sorting structure pointers */
971 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
973 /* writing structure pointers to sorting array */
974 while (i < num_nodes && node) /* double boundary check... */
976 sort_array[i] = node;
982 /* sorting the structure pointers in the sorting array */
983 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
986 /* update the linkage of list elements with the sorted node array */
987 for (i = 0; i < num_nodes - 1; i++)
988 sort_array[i]->next = sort_array[i + 1];
989 sort_array[num_nodes - 1]->next = NULL;
991 /* update the linkage of the main list anchor pointer */
992 *node_first = sort_array[0];
996 /* now recursively sort the level group structures */
1000 if (node->node_group != NULL)
1001 sortTreeInfo(&node->node_group, compare_function);
1008 /* ========================================================================= */
1009 /* some stuff from "files.c" */
1010 /* ========================================================================= */
1012 #if defined(PLATFORM_WIN32)
1014 #define S_IRGRP S_IRUSR
1017 #define S_IROTH S_IRUSR
1020 #define S_IWGRP S_IWUSR
1023 #define S_IWOTH S_IWUSR
1026 #define S_IXGRP S_IXUSR
1029 #define S_IXOTH S_IXUSR
1032 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1037 #endif /* PLATFORM_WIN32 */
1039 /* file permissions for newly written files */
1040 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1041 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1042 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1044 #define MODE_W_PRIVATE (S_IWUSR)
1045 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1046 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1048 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1049 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1051 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1052 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1054 char *getUserDataDir(void)
1056 static char *userdata_dir = NULL;
1058 if (userdata_dir == NULL)
1059 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1061 return userdata_dir;
1064 char *getCommonDataDir(void)
1066 static char *common_data_dir = NULL;
1068 #if defined(PLATFORM_WIN32)
1069 if (common_data_dir == NULL)
1071 char *dir = checked_malloc(MAX_PATH + 1);
1073 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1074 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1075 common_data_dir = getPath2(dir, program.userdata_directory);
1077 common_data_dir = options.rw_base_directory;
1080 if (common_data_dir == NULL)
1081 common_data_dir = options.rw_base_directory;
1084 return common_data_dir;
1089 return getUserDataDir();
1092 static mode_t posix_umask(mode_t mask)
1094 #if defined(PLATFORM_UNIX)
1101 static int posix_mkdir(const char *pathname, mode_t mode)
1103 #if defined(PLATFORM_WIN32)
1104 return mkdir(pathname);
1106 return mkdir(pathname, mode);
1110 void createDirectory(char *dir, char *text, int permission_class)
1112 /* leave "other" permissions in umask untouched, but ensure group parts
1113 of USERDATA_DIR_MODE are not masked */
1114 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1115 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1116 mode_t normal_umask = posix_umask(0);
1117 mode_t group_umask = ~(dir_mode & S_IRWXG);
1118 posix_umask(normal_umask & group_umask);
1120 if (access(dir, F_OK) != 0)
1121 if (posix_mkdir(dir, dir_mode) != 0)
1122 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1124 posix_umask(normal_umask); /* reset normal umask */
1127 void InitUserDataDirectory()
1129 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1132 void SetFilePermissions(char *filename, int permission_class)
1134 chmod(filename, (permission_class == PERMS_PRIVATE ?
1135 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1138 char *getCookie(char *file_type)
1140 static char cookie[MAX_COOKIE_LEN + 1];
1142 if (strlen(program.cookie_prefix) + 1 +
1143 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1144 return "[COOKIE ERROR]"; /* should never happen */
1146 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1147 program.cookie_prefix, file_type,
1148 program.version_major, program.version_minor);
1153 int getFileVersionFromCookieString(const char *cookie)
1155 const char *ptr_cookie1, *ptr_cookie2;
1156 const char *pattern1 = "_FILE_VERSION_";
1157 const char *pattern2 = "?.?";
1158 const int len_cookie = strlen(cookie);
1159 const int len_pattern1 = strlen(pattern1);
1160 const int len_pattern2 = strlen(pattern2);
1161 const int len_pattern = len_pattern1 + len_pattern2;
1162 int version_major, version_minor;
1164 if (len_cookie <= len_pattern)
1167 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1168 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1170 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1173 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1174 ptr_cookie2[1] != '.' ||
1175 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1178 version_major = ptr_cookie2[0] - '0';
1179 version_minor = ptr_cookie2[2] - '0';
1181 return VERSION_IDENT(version_major, version_minor, 0, 0);
1184 boolean checkCookieString(const char *cookie, const char *template)
1186 const char *pattern = "_FILE_VERSION_?.?";
1187 const int len_cookie = strlen(cookie);
1188 const int len_template = strlen(template);
1189 const int len_pattern = strlen(pattern);
1191 if (len_cookie != len_template)
1194 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1200 /* ------------------------------------------------------------------------- */
1201 /* setup file list and hash handling functions */
1202 /* ------------------------------------------------------------------------- */
1204 char *getFormattedSetupEntry(char *token, char *value)
1207 static char entry[MAX_LINE_LEN];
1209 /* start with the token and some spaces to format output line */
1210 sprintf(entry, "%s:", token);
1211 for (i = strlen(entry); i < TOKEN_VALUE_POSITION; i++)
1214 /* continue with the token's value */
1215 strcat(entry, value);
1220 SetupFileList *newSetupFileList(char *token, char *value)
1222 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1224 new->token = getStringCopy(token);
1225 new->value = getStringCopy(value);
1232 void freeSetupFileList(SetupFileList *list)
1242 freeSetupFileList(list->next);
1246 char *getListEntry(SetupFileList *list, char *token)
1251 if (strcmp(list->token, token) == 0)
1254 return getListEntry(list->next, token);
1257 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1262 if (strcmp(list->token, token) == 0)
1267 list->value = getStringCopy(value);
1271 else if (list->next == NULL)
1272 return (list->next = newSetupFileList(token, value));
1274 return setListEntry(list->next, token, value);
1277 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1282 if (list->next == NULL)
1283 return (list->next = newSetupFileList(token, value));
1285 return addListEntry(list->next, token, value);
1289 static void printSetupFileList(SetupFileList *list)
1294 printf("token: '%s'\n", list->token);
1295 printf("value: '%s'\n", list->value);
1297 printSetupFileList(list->next);
1302 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1303 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1304 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1305 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1307 #define insert_hash_entry hashtable_insert
1308 #define search_hash_entry hashtable_search
1309 #define change_hash_entry hashtable_change
1310 #define remove_hash_entry hashtable_remove
1313 static unsigned int get_hash_from_key(void *key)
1318 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1319 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1320 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1321 it works better than many other constants, prime or not) has never been
1322 adequately explained.
1324 If you just want to have a good hash function, and cannot wait, djb2
1325 is one of the best string hash functions i know. It has excellent
1326 distribution and speed on many different sets of keys and table sizes.
1327 You are not likely to do better with one of the "well known" functions
1328 such as PJW, K&R, etc.
1330 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1333 char *str = (char *)key;
1334 unsigned int hash = 5381;
1337 while ((c = *str++))
1338 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1343 static int keys_are_equal(void *key1, void *key2)
1345 return (strcmp((char *)key1, (char *)key2) == 0);
1348 SetupFileHash *newSetupFileHash()
1350 SetupFileHash *new_hash =
1351 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1353 if (new_hash == NULL)
1354 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1359 void freeSetupFileHash(SetupFileHash *hash)
1364 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1367 char *getHashEntry(SetupFileHash *hash, char *token)
1372 return search_hash_entry(hash, token);
1375 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1382 value_copy = getStringCopy(value);
1384 /* change value; if it does not exist, insert it as new */
1385 if (!change_hash_entry(hash, token, value_copy))
1386 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1387 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1392 static void printSetupFileHash(SetupFileHash *hash)
1394 BEGIN_HASH_ITERATION(hash, itr)
1396 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1397 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1399 END_HASH_ITERATION(hash, itr)
1404 static void *loadSetupFileData(char *filename, boolean use_hash)
1407 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1408 char *token, *value, *line_ptr;
1409 void *setup_file_data, *insert_ptr = NULL;
1410 boolean read_continued_line = FALSE;
1414 setup_file_data = newSetupFileHash();
1416 insert_ptr = setup_file_data = newSetupFileList("", "");
1418 if (!(file = fopen(filename, MODE_READ)))
1420 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1426 /* read next line of input file */
1427 if (!fgets(line, MAX_LINE_LEN, file))
1430 /* cut trailing newline or carriage return */
1431 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1432 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1435 if (read_continued_line)
1437 /* cut leading whitespaces from input line */
1438 for (line_ptr = line; *line_ptr; line_ptr++)
1439 if (*line_ptr != ' ' && *line_ptr != '\t')
1442 /* append new line to existing line, if there is enough space */
1443 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1444 strcat(previous_line, line_ptr);
1446 strcpy(line, previous_line); /* copy storage buffer to line */
1448 read_continued_line = FALSE;
1451 /* if the last character is '\', continue at next line */
1452 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1454 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1455 strcpy(previous_line, line); /* copy line to storage buffer */
1457 read_continued_line = TRUE;
1462 /* cut trailing comment from input line */
1463 for (line_ptr = line; *line_ptr; line_ptr++)
1465 if (*line_ptr == '#')
1472 /* cut trailing whitespaces from input line */
1473 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1474 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1477 /* ignore empty lines */
1481 line_len = strlen(line);
1483 /* cut leading whitespaces from token */
1484 for (token = line; *token; token++)
1485 if (*token != ' ' && *token != '\t')
1488 /* find end of token */
1489 for (line_ptr = token; *line_ptr; line_ptr++)
1491 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1498 if (line_ptr < line + line_len)
1499 value = line_ptr + 1;
1502 value = "true"; /* treat tokens without value as "true" */
1507 /* cut leading whitespaces from value */
1508 for (; *value; value++)
1509 if (*value != ' ' && *value != '\t')
1512 if (*token && *value)
1515 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1517 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1525 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1526 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1530 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1531 SetupFileList *first_valid_list_entry = setup_file_list->next;
1533 /* free empty list header */
1534 setup_file_list->next = NULL;
1535 freeSetupFileList(setup_file_list);
1536 setup_file_data = first_valid_list_entry;
1538 if (first_valid_list_entry == NULL)
1539 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1542 return setup_file_data;
1545 SetupFileList *loadSetupFileList(char *filename)
1547 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1550 SetupFileHash *loadSetupFileHash(char *filename)
1552 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1555 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1558 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1561 Error(ERR_WARN, "configuration file has no file identifier");
1562 else if (!checkCookieString(value, identifier))
1563 Error(ERR_WARN, "configuration file has wrong file identifier");
1567 /* ========================================================================= */
1568 /* setup file stuff */
1569 /* ========================================================================= */
1571 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1572 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1573 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1575 /* level directory info */
1576 #define LEVELINFO_TOKEN_IDENTIFIER 0
1577 #define LEVELINFO_TOKEN_NAME 1
1578 #define LEVELINFO_TOKEN_NAME_SORTING 2
1579 #define LEVELINFO_TOKEN_AUTHOR 3
1580 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1581 #define LEVELINFO_TOKEN_LEVELS 5
1582 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1583 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1584 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1585 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1586 #define LEVELINFO_TOKEN_READONLY 10
1587 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1588 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1589 #define LEVELINFO_TOKEN_MUSIC_SET 13
1591 #define NUM_LEVELINFO_TOKENS 14
1593 static LevelDirTree ldi;
1595 static struct TokenInfo levelinfo_tokens[] =
1597 /* level directory info */
1598 { TYPE_STRING, &ldi.identifier, "identifier" },
1599 { TYPE_STRING, &ldi.name, "name" },
1600 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1601 { TYPE_STRING, &ldi.author, "author" },
1602 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1603 { TYPE_INTEGER, &ldi.levels, "levels" },
1604 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1605 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1606 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1607 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1608 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1609 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1610 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1611 { TYPE_STRING, &ldi.music_set, "music_set" }
1614 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1618 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1619 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1620 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1621 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1624 ldi->node_parent = NULL;
1625 ldi->node_group = NULL;
1629 ldi->cl_cursor = -1;
1631 ldi->filename = NULL;
1632 ldi->fullpath = NULL;
1633 ldi->basepath = NULL;
1634 ldi->identifier = NULL;
1635 ldi->name = getStringCopy(ANONYMOUS_NAME);
1636 ldi->name_sorting = NULL;
1637 ldi->author = getStringCopy(ANONYMOUS_NAME);
1639 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1640 ldi->latest_engine = FALSE; /* default: get from level */
1641 ldi->parent_link = FALSE;
1642 ldi->user_defined = FALSE;
1644 ldi->class_desc = NULL;
1646 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1648 ldi->imported_from = NULL;
1650 ldi->graphics_set = NULL;
1651 ldi->sounds_set = NULL;
1652 ldi->music_set = NULL;
1653 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1654 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1655 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1658 ldi->first_level = 0;
1659 ldi->last_level = 0;
1660 ldi->level_group = FALSE;
1661 ldi->handicap_level = 0;
1662 ldi->readonly = TRUE;
1666 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1670 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1672 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1678 /* copy all values from the parent structure */
1680 ldi->type = parent->type;
1682 ldi->node_top = parent->node_top;
1683 ldi->node_parent = parent;
1684 ldi->node_group = NULL;
1688 ldi->cl_cursor = -1;
1690 ldi->filename = NULL;
1691 ldi->fullpath = NULL;
1692 ldi->basepath = NULL;
1693 ldi->identifier = NULL;
1694 ldi->name = getStringCopy(ANONYMOUS_NAME);
1695 ldi->name_sorting = NULL;
1696 ldi->author = getStringCopy(parent->author);
1698 ldi->sort_priority = parent->sort_priority;
1699 ldi->latest_engine = parent->latest_engine;
1700 ldi->parent_link = FALSE;
1701 ldi->user_defined = parent->user_defined;
1702 ldi->color = parent->color;
1703 ldi->class_desc = getStringCopy(parent->class_desc);
1705 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1707 ldi->imported_from = getStringCopy(parent->imported_from);
1709 ldi->graphics_set = NULL;
1710 ldi->sounds_set = NULL;
1711 ldi->music_set = NULL;
1712 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1713 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1714 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1717 ldi->first_level = 0;
1718 ldi->last_level = 0;
1719 ldi->level_group = FALSE;
1720 ldi->handicap_level = 0;
1721 ldi->readonly = TRUE;
1727 /* first copy all values from the parent structure ... */
1730 /* ... then set all fields to default that cannot be inherited from parent.
1731 This is especially important for all those fields that can be set from
1732 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1733 calls 'free()' for all already set token values which requires that no
1734 other structure's pointer may point to them!
1737 ldi->filename = NULL;
1738 ldi->fullpath = NULL;
1739 ldi->basepath = NULL;
1740 ldi->identifier = NULL;
1741 ldi->name = getStringCopy(ANONYMOUS_NAME);
1742 ldi->name_sorting = NULL;
1743 ldi->author = getStringCopy(parent->author);
1745 ldi->imported_from = getStringCopy(parent->imported_from);
1746 ldi->class_desc = getStringCopy(parent->class_desc);
1748 ldi->graphics_set = NULL;
1749 ldi->sounds_set = NULL;
1750 ldi->music_set = NULL;
1751 ldi->graphics_path = NULL;
1752 ldi->sounds_path = NULL;
1753 ldi->music_path = NULL;
1755 ldi->level_group = FALSE;
1756 ldi->parent_link = FALSE;
1758 ldi->node_top = parent->node_top;
1759 ldi->node_parent = parent;
1760 ldi->node_group = NULL;
1766 static void freeTreeInfo(TreeInfo *ldi)
1769 free(ldi->filename);
1771 free(ldi->fullpath);
1773 free(ldi->basepath);
1774 if (ldi->identifier)
1775 free(ldi->identifier);
1779 if (ldi->name_sorting)
1780 free(ldi->name_sorting);
1784 if (ldi->class_desc)
1785 free(ldi->class_desc);
1787 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1789 if (ldi->graphics_set)
1790 free(ldi->graphics_set);
1791 if (ldi->sounds_set)
1792 free(ldi->sounds_set);
1794 free(ldi->music_set);
1796 if (ldi->graphics_path)
1797 free(ldi->graphics_path);
1798 if (ldi->sounds_path)
1799 free(ldi->sounds_path);
1800 if (ldi->music_path)
1801 free(ldi->music_path);
1805 void setSetupInfo(struct TokenInfo *token_info,
1806 int token_nr, char *token_value)
1808 int token_type = token_info[token_nr].type;
1809 void *setup_value = token_info[token_nr].value;
1811 if (token_value == NULL)
1814 /* set setup field to corresponding token value */
1819 *(boolean *)setup_value = get_boolean_from_string(token_value);
1823 *(Key *)setup_value = getKeyFromKeyName(token_value);
1827 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1831 *(int *)setup_value = get_integer_from_string(token_value);
1835 if (*(char **)setup_value != NULL)
1836 free(*(char **)setup_value);
1837 *(char **)setup_value = getStringCopy(token_value);
1845 static int compareTreeInfoEntries(const void *object1, const void *object2)
1847 const TreeInfo *entry1 = *((TreeInfo **)object1);
1848 const TreeInfo *entry2 = *((TreeInfo **)object2);
1849 int class_sorting1, class_sorting2;
1852 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1854 class_sorting1 = LEVELSORTING(entry1);
1855 class_sorting2 = LEVELSORTING(entry2);
1859 class_sorting1 = ARTWORKSORTING(entry1);
1860 class_sorting2 = ARTWORKSORTING(entry2);
1863 if (entry1->parent_link || entry2->parent_link)
1864 compare_result = (entry1->parent_link ? -1 : +1);
1865 else if (entry1->sort_priority == entry2->sort_priority)
1867 char *name1 = getStringToLower(entry1->name_sorting);
1868 char *name2 = getStringToLower(entry2->name_sorting);
1870 compare_result = strcmp(name1, name2);
1875 else if (class_sorting1 == class_sorting2)
1876 compare_result = entry1->sort_priority - entry2->sort_priority;
1878 compare_result = class_sorting1 - class_sorting2;
1880 return compare_result;
1883 static void createParentTreeInfoNode(TreeInfo *node_parent)
1887 if (node_parent == NULL)
1890 ti_new = newTreeInfo();
1891 setTreeInfoToDefaults(ti_new, node_parent->type);
1893 ti_new->node_parent = node_parent;
1894 ti_new->parent_link = TRUE;
1897 setString(&ti_new->identifier, node_parent->identifier);
1898 setString(&ti_new->name, ".. (parent directory)");
1899 setString(&ti_new->name_sorting, ti_new->name);
1901 setString(&ti_new->filename, "..");
1902 setString(&ti_new->fullpath, node_parent->fullpath);
1904 ti_new->sort_priority = node_parent->sort_priority;
1905 ti_new->latest_engine = node_parent->latest_engine;
1907 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1909 ti_new->identifier = getStringCopy(node_parent->identifier);
1910 ti_new->name = ".. (parent directory)";
1911 ti_new->name_sorting = getStringCopy(ti_new->name);
1913 ti_new->filename = "..";
1914 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1916 ti_new->sort_priority = node_parent->sort_priority;
1917 ti_new->latest_engine = node_parent->latest_engine;
1919 ti_new->class_desc = getLevelClassDescription(ti_new);
1922 pushTreeInfo(&node_parent->node_group, ti_new);
1925 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1926 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1928 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1929 TreeInfo *node_parent,
1930 char *level_directory,
1931 char *directory_name)
1933 char *directory_path = getPath2(level_directory, directory_name);
1934 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1935 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1936 LevelDirTree *leveldir_new = NULL;
1939 if (setup_file_hash == NULL)
1941 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1943 free(directory_path);
1949 leveldir_new = newTreeInfo();
1952 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1954 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1956 leveldir_new->filename = getStringCopy(directory_name);
1958 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1960 /* set all structure fields according to the token/value pairs */
1961 ldi = *leveldir_new;
1962 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
1963 setSetupInfo(levelinfo_tokens, i,
1964 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1965 *leveldir_new = ldi;
1968 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1969 setString(&leveldir_new->name, leveldir_new->filename);
1971 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1973 free(leveldir_new->name);
1974 leveldir_new->name = getStringCopy(leveldir_new->filename);
1978 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1980 if (leveldir_new->identifier == NULL)
1981 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1983 if (leveldir_new->name_sorting == NULL)
1984 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1986 if (node_parent == NULL) /* top level group */
1988 leveldir_new->basepath = getStringCopy(level_directory);
1989 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1991 else /* sub level group */
1993 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1994 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1997 if (leveldir_new->levels < 1)
1998 leveldir_new->levels = 1;
2000 leveldir_new->last_level =
2001 leveldir_new->first_level + leveldir_new->levels - 1;
2004 leveldir_new->user_defined =
2005 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2007 leveldir_new->user_defined =
2008 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
2011 leveldir_new->color = LEVELCOLOR(leveldir_new);
2013 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2015 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
2018 leveldir_new->handicap_level = /* set handicap to default value */
2019 (leveldir_new->user_defined ?
2020 leveldir_new->last_level :
2021 leveldir_new->first_level);
2023 pushTreeInfo(node_first, leveldir_new);
2025 freeSetupFileHash(setup_file_hash);
2027 if (leveldir_new->level_group)
2029 /* create node to link back to current level directory */
2030 createParentTreeInfoNode(leveldir_new);
2032 /* step into sub-directory and look for more level series */
2033 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2034 leveldir_new, directory_path);
2037 free(directory_path);
2043 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2044 TreeInfo *node_parent,
2045 char *level_directory)
2048 struct dirent *dir_entry;
2049 boolean valid_entry_found = FALSE;
2051 if ((dir = opendir(level_directory)) == NULL)
2053 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2057 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2059 struct stat file_status;
2060 char *directory_name = dir_entry->d_name;
2061 char *directory_path = getPath2(level_directory, directory_name);
2063 /* skip entries for current and parent directory */
2064 if (strcmp(directory_name, ".") == 0 ||
2065 strcmp(directory_name, "..") == 0)
2067 free(directory_path);
2071 /* find out if directory entry is itself a directory */
2072 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2073 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2075 free(directory_path);
2079 free(directory_path);
2081 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2082 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2083 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2086 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2093 if (!valid_entry_found)
2095 /* check if this directory directly contains a file "levelinfo.conf" */
2096 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2097 level_directory, ".");
2100 if (!valid_entry_found)
2101 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2105 void LoadLevelInfo()
2107 InitUserLevelDirectory(getLoginName());
2109 DrawInitText("Loading level series:", 120, FC_GREEN);
2111 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2112 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2114 /* before sorting, the first entries will be from the user directory */
2115 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2117 if (leveldir_first == NULL)
2118 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2120 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2123 dumpTreeInfo(leveldir_first, 0);
2127 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2128 TreeInfo *node_parent,
2129 char *base_directory,
2130 char *directory_name, int type)
2132 char *directory_path = getPath2(base_directory, directory_name);
2133 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2134 SetupFileHash *setup_file_hash = NULL;
2135 TreeInfo *artwork_new = NULL;
2138 if (access(filename, F_OK) == 0) /* file exists */
2139 setup_file_hash = loadSetupFileHash(filename);
2141 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2144 struct dirent *dir_entry;
2145 boolean valid_file_found = FALSE;
2147 if ((dir = opendir(directory_path)) != NULL)
2149 while ((dir_entry = readdir(dir)) != NULL)
2151 char *entry_name = dir_entry->d_name;
2153 if (FileIsArtworkType(entry_name, type))
2155 valid_file_found = TRUE;
2163 if (!valid_file_found)
2165 if (strcmp(directory_name, ".") != 0)
2166 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2168 free(directory_path);
2175 artwork_new = newTreeInfo();
2178 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2180 setTreeInfoToDefaults(artwork_new, type);
2182 artwork_new->filename = getStringCopy(directory_name);
2184 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2187 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2190 /* set all structure fields according to the token/value pairs */
2192 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2193 setSetupInfo(levelinfo_tokens, i,
2194 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2198 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2199 setString(&artwork_new->name, artwork_new->filename);
2201 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2203 free(artwork_new->name);
2204 artwork_new->name = getStringCopy(artwork_new->filename);
2209 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2212 if (artwork_new->identifier == NULL)
2213 artwork_new->identifier = getStringCopy(artwork_new->filename);
2215 if (artwork_new->name_sorting == NULL)
2216 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2219 if (node_parent == NULL) /* top level group */
2221 artwork_new->basepath = getStringCopy(base_directory);
2222 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2224 else /* sub level group */
2226 artwork_new->basepath = getStringCopy(node_parent->basepath);
2227 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2231 artwork_new->user_defined =
2232 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2234 artwork_new->user_defined =
2235 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2238 /* (may use ".sort_priority" from "setup_file_hash" above) */
2239 artwork_new->color = ARTWORKCOLOR(artwork_new);
2241 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2243 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2246 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2249 if (artwork_new->name != NULL)
2251 free(artwork_new->name);
2252 artwork_new->name = NULL;
2257 if (artwork_new->identifier != NULL)
2259 free(artwork_new->identifier);
2260 artwork_new->identifier = NULL;
2264 if (strcmp(artwork_new->filename, ".") == 0)
2266 if (artwork_new->user_defined)
2269 setString(&artwork_new->identifier, "private");
2271 artwork_new->identifier = getStringCopy("private");
2273 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2278 setString(&artwork_new->identifier, "classic");
2280 artwork_new->identifier = getStringCopy("classic");
2282 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2285 /* set to new values after changing ".sort_priority" */
2286 artwork_new->color = ARTWORKCOLOR(artwork_new);
2288 setString(&artwork_new->class_desc,
2289 getLevelClassDescription(artwork_new));
2291 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2297 setString(&artwork_new->identifier, artwork_new->filename);
2299 artwork_new->identifier = getStringCopy(artwork_new->filename);
2304 setString(&artwork_new->name, artwork_new->identifier);
2305 setString(&artwork_new->name_sorting, artwork_new->name);
2307 artwork_new->name = getStringCopy(artwork_new->identifier);
2308 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2312 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2314 pushTreeInfo(node_first, artwork_new);
2316 freeSetupFileHash(setup_file_hash);
2318 free(directory_path);
2324 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2325 TreeInfo *node_parent,
2326 char *base_directory, int type)
2329 struct dirent *dir_entry;
2330 boolean valid_entry_found = FALSE;
2332 if ((dir = opendir(base_directory)) == NULL)
2334 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2335 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2339 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2341 struct stat file_status;
2342 char *directory_name = dir_entry->d_name;
2343 char *directory_path = getPath2(base_directory, directory_name);
2345 /* skip entries for current and parent directory */
2346 if (strcmp(directory_name, ".") == 0 ||
2347 strcmp(directory_name, "..") == 0)
2349 free(directory_path);
2353 /* find out if directory entry is itself a directory */
2354 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2355 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2357 free(directory_path);
2361 free(directory_path);
2363 /* check if this directory contains artwork with or without config file */
2364 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2366 directory_name, type);
2371 /* check if this directory directly contains artwork itself */
2372 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2373 base_directory, ".",
2375 if (!valid_entry_found)
2376 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2380 static TreeInfo *getDummyArtworkInfo(int type)
2382 /* this is only needed when there is completely no artwork available */
2383 TreeInfo *artwork_new = newTreeInfo();
2385 setTreeInfoToDefaults(artwork_new, type);
2388 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2389 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2390 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2392 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2393 setString(&artwork_new->name, UNDEFINED_FILENAME);
2394 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2396 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2397 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2398 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2400 if (artwork_new->name != NULL)
2401 free(artwork_new->name);
2403 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2404 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2405 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2411 void LoadArtworkInfo()
2413 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2415 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2416 options.graphics_directory,
2417 TREE_TYPE_GRAPHICS_DIR);
2418 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2419 getUserGraphicsDir(),
2420 TREE_TYPE_GRAPHICS_DIR);
2422 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2423 options.sounds_directory,
2424 TREE_TYPE_SOUNDS_DIR);
2425 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2427 TREE_TYPE_SOUNDS_DIR);
2429 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2430 options.music_directory,
2431 TREE_TYPE_MUSIC_DIR);
2432 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2434 TREE_TYPE_MUSIC_DIR);
2436 if (artwork.gfx_first == NULL)
2437 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2438 if (artwork.snd_first == NULL)
2439 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2440 if (artwork.mus_first == NULL)
2441 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2443 /* before sorting, the first entries will be from the user directory */
2444 artwork.gfx_current =
2445 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2446 if (artwork.gfx_current == NULL)
2447 artwork.gfx_current =
2448 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2449 if (artwork.gfx_current == NULL)
2450 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2452 artwork.snd_current =
2453 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2454 if (artwork.snd_current == NULL)
2455 artwork.snd_current =
2456 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2457 if (artwork.snd_current == NULL)
2458 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2460 artwork.mus_current =
2461 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2462 if (artwork.mus_current == NULL)
2463 artwork.mus_current =
2464 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2465 if (artwork.mus_current == NULL)
2466 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2468 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2469 artwork.snd_current_identifier = artwork.snd_current->identifier;
2470 artwork.mus_current_identifier = artwork.mus_current->identifier;
2473 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2474 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2475 printf("music set == %s\n\n", artwork.mus_current_identifier);
2478 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2479 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2480 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2483 dumpTreeInfo(artwork.gfx_first, 0);
2484 dumpTreeInfo(artwork.snd_first, 0);
2485 dumpTreeInfo(artwork.mus_first, 0);
2489 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2490 LevelDirTree *level_node)
2492 /* recursively check all level directories for artwork sub-directories */
2496 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2497 ARTWORK_DIRECTORY((*artwork_node)->type));
2500 if (!level_node->parent_link)
2501 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2502 level_node->filename, level_node->name);
2505 if (!level_node->parent_link)
2507 TreeInfo *topnode_last = *artwork_node;
2509 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2510 (*artwork_node)->type);
2512 if (topnode_last != *artwork_node)
2514 free((*artwork_node)->identifier);
2515 free((*artwork_node)->name);
2516 free((*artwork_node)->name_sorting);
2518 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2519 (*artwork_node)->name = getStringCopy(level_node->name);
2520 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2522 (*artwork_node)->sort_priority = level_node->sort_priority;
2523 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2529 if (level_node->node_group != NULL)
2530 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2532 level_node = level_node->next;
2536 void LoadLevelArtworkInfo()
2538 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2540 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2541 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2542 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2544 /* needed for reloading level artwork not known at ealier stage */
2546 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2548 artwork.gfx_current =
2549 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2550 if (artwork.gfx_current == NULL)
2551 artwork.gfx_current =
2552 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2553 if (artwork.gfx_current == NULL)
2554 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2557 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2559 artwork.snd_current =
2560 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2561 if (artwork.snd_current == NULL)
2562 artwork.snd_current =
2563 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2564 if (artwork.snd_current == NULL)
2565 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2568 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2570 artwork.mus_current =
2571 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2572 if (artwork.mus_current == NULL)
2573 artwork.mus_current =
2574 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2575 if (artwork.mus_current == NULL)
2576 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2579 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2580 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2581 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2584 dumpTreeInfo(artwork.gfx_first, 0);
2585 dumpTreeInfo(artwork.snd_first, 0);
2586 dumpTreeInfo(artwork.mus_first, 0);
2590 static void SaveUserLevelInfo()
2592 LevelDirTree *level_info;
2597 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2599 if (!(file = fopen(filename, MODE_WRITE)))
2601 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2606 level_info = newTreeInfo();
2608 /* always start with reliable default values */
2609 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2612 setString(&level_info->name, getLoginName());
2613 setString(&level_info->author, getRealName());
2614 level_info->levels = 100;
2615 level_info->first_level = 1;
2616 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2617 level_info->readonly = FALSE;
2618 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2619 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2620 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2622 ldi.name = getStringCopy(getLoginName());
2623 ldi.author = getStringCopy(getRealName());
2625 ldi.first_level = 1;
2626 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2627 ldi.readonly = FALSE;
2628 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2629 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2630 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2633 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2634 getCookie("LEVELINFO")));
2637 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2638 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2639 i != LEVELINFO_TOKEN_NAME_SORTING &&
2640 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2641 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2645 SetFilePermissions(filename, PERMS_PRIVATE);
2647 freeTreeInfo(level_info);
2651 char *getSetupValue(int type, void *value)
2653 static char value_string[MAX_LINE_LEN];
2661 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2665 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2669 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2673 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2677 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2681 sprintf(value_string, "%d", *(int *)value);
2685 strcpy(value_string, *(char **)value);
2689 value_string[0] = '\0';
2693 return value_string;
2696 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2700 static char token_string[MAX_LINE_LEN];
2701 int token_type = token_info[token_nr].type;
2702 void *setup_value = token_info[token_nr].value;
2703 char *token_text = token_info[token_nr].text;
2704 char *value_string = getSetupValue(token_type, setup_value);
2706 /* build complete token string */
2707 sprintf(token_string, "%s%s", prefix, token_text);
2709 /* build setup entry line */
2710 line = getFormattedSetupEntry(token_string, value_string);
2712 if (token_type == TYPE_KEY_X11)
2714 Key key = *(Key *)setup_value;
2715 char *keyname = getKeyNameFromKey(key);
2717 /* add comment, if useful */
2718 if (strcmp(keyname, "(undefined)") != 0 &&
2719 strcmp(keyname, "(unknown)") != 0)
2721 /* add at least one whitespace */
2723 for (i = strlen(line); i < TOKEN_COMMENT_POSITION; i++)
2727 strcat(line, keyname);
2734 void LoadLevelSetup_LastSeries()
2736 /* ----------------------------------------------------------------------- */
2737 /* ~/.<program>/levelsetup.conf */
2738 /* ----------------------------------------------------------------------- */
2740 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2741 SetupFileHash *level_setup_hash = NULL;
2743 /* always start with reliable default values */
2744 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2746 if ((level_setup_hash = loadSetupFileHash(filename)))
2748 char *last_level_series =
2749 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2751 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2753 if (leveldir_current == NULL)
2754 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2756 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2758 freeSetupFileHash(level_setup_hash);
2761 Error(ERR_WARN, "using default setup values");
2766 void SaveLevelSetup_LastSeries()
2768 /* ----------------------------------------------------------------------- */
2769 /* ~/.<program>/levelsetup.conf */
2770 /* ----------------------------------------------------------------------- */
2772 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2773 char *level_subdir = leveldir_current->filename;
2776 InitUserDataDirectory();
2778 if (!(file = fopen(filename, MODE_WRITE)))
2780 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2785 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2786 getCookie("LEVELSETUP")));
2787 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2792 SetFilePermissions(filename, PERMS_PRIVATE);
2797 static void checkSeriesInfo()
2799 static char *level_directory = NULL;
2801 struct dirent *dir_entry;
2803 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2805 level_directory = getPath2((leveldir_current->user_defined ?
2806 getUserLevelDir(NULL) :
2807 options.level_directory),
2808 leveldir_current->fullpath);
2810 if ((dir = opendir(level_directory)) == NULL)
2812 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2816 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2818 if (strlen(dir_entry->d_name) > 4 &&
2819 dir_entry->d_name[3] == '.' &&
2820 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2822 char levelnum_str[4];
2825 strncpy(levelnum_str, dir_entry->d_name, 3);
2826 levelnum_str[3] = '\0';
2828 levelnum_value = atoi(levelnum_str);
2831 if (levelnum_value < leveldir_current->first_level)
2833 Error(ERR_WARN, "additional level %d found", levelnum_value);
2834 leveldir_current->first_level = levelnum_value;
2836 else if (levelnum_value > leveldir_current->last_level)
2838 Error(ERR_WARN, "additional level %d found", levelnum_value);
2839 leveldir_current->last_level = levelnum_value;
2848 void LoadLevelSetup_SeriesInfo()
2851 SetupFileHash *level_setup_hash = NULL;
2852 char *level_subdir = leveldir_current->filename;
2854 /* always start with reliable default values */
2855 level_nr = leveldir_current->first_level;
2857 checkSeriesInfo(leveldir_current);
2859 /* ----------------------------------------------------------------------- */
2860 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2861 /* ----------------------------------------------------------------------- */
2863 level_subdir = leveldir_current->filename;
2865 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2867 if ((level_setup_hash = loadSetupFileHash(filename)))
2871 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2875 level_nr = atoi(token_value);
2877 if (level_nr < leveldir_current->first_level)
2878 level_nr = leveldir_current->first_level;
2879 if (level_nr > leveldir_current->last_level)
2880 level_nr = leveldir_current->last_level;
2883 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2887 int level_nr = atoi(token_value);
2889 if (level_nr < leveldir_current->first_level)
2890 level_nr = leveldir_current->first_level;
2891 if (level_nr > leveldir_current->last_level + 1)
2892 level_nr = leveldir_current->last_level;
2894 if (leveldir_current->user_defined)
2895 level_nr = leveldir_current->last_level;
2897 leveldir_current->handicap_level = level_nr;
2900 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2902 freeSetupFileHash(level_setup_hash);
2905 Error(ERR_WARN, "using default setup values");
2910 void SaveLevelSetup_SeriesInfo()
2913 char *level_subdir = leveldir_current->filename;
2914 char *level_nr_str = int2str(level_nr, 0);
2915 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2918 /* ----------------------------------------------------------------------- */
2919 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2920 /* ----------------------------------------------------------------------- */
2922 InitLevelSetupDirectory(level_subdir);
2924 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2926 if (!(file = fopen(filename, MODE_WRITE)))
2928 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2933 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2934 getCookie("LEVELSETUP")));
2935 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2937 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2938 handicap_level_str));
2942 SetFilePermissions(filename, PERMS_PRIVATE);