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 *getDemoAnimInfoFilename()
422 static char *filename = NULL;
424 if (filename != NULL)
427 filename = getPath2(getCurrentLevelDir(), DEMOANIMINFO_FILENAME);
432 char *getDemoAnimTextFilename()
434 static char *filename = NULL;
436 if (filename != NULL)
439 filename = getPath2(getCurrentLevelDir(), DEMOANIMTEXT_FILENAME);
444 static char *getCorrectedArtworkBasename(char *basename)
446 char *basename_corrected = basename;
448 #if defined(PLATFORM_MSDOS)
449 if (program.filename_prefix != NULL)
451 int prefix_len = strlen(program.filename_prefix);
453 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
454 basename_corrected = &basename[prefix_len];
456 /* if corrected filename is still longer than standard MS-DOS filename
457 size (8 characters + 1 dot + 3 characters file extension), shorten
458 filename by writing file extension after 8th basename character */
459 if (strlen(basename_corrected) > 8 + 1 + 3)
461 static char *msdos_filename = NULL;
463 if (msdos_filename != NULL)
464 free(msdos_filename);
466 msdos_filename = getStringCopy(basename_corrected);
467 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
469 basename_corrected = msdos_filename;
474 return basename_corrected;
477 char *getCustomImageFilename(char *basename)
479 static char *filename = NULL;
480 boolean skip_setup_artwork = FALSE;
482 if (filename != NULL)
485 basename = getCorrectedArtworkBasename(basename);
487 if (!setup.override_level_graphics)
489 /* 1st try: look for special artwork in current level series directory */
490 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
491 if (fileExists(filename))
496 /* check if there is special artwork configured in level series config */
497 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
499 /* 2nd try: look for special artwork configured in level series config */
500 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
501 if (fileExists(filename))
506 /* take missing artwork configured in level set config from default */
507 skip_setup_artwork = TRUE;
511 if (!skip_setup_artwork)
513 /* 3rd try: look for special artwork in configured artwork directory */
514 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
515 if (fileExists(filename))
521 /* 4th try: look for default artwork in new default artwork directory */
522 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
523 if (fileExists(filename))
528 /* 5th try: look for default artwork in old default artwork directory */
529 filename = getPath2(options.graphics_directory, basename);
530 if (fileExists(filename))
533 return NULL; /* cannot find specified artwork file anywhere */
536 char *getCustomSoundFilename(char *basename)
538 static char *filename = NULL;
539 boolean skip_setup_artwork = FALSE;
541 if (filename != NULL)
544 basename = getCorrectedArtworkBasename(basename);
546 if (!setup.override_level_sounds)
548 /* 1st try: look for special artwork in current level series directory */
549 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
550 if (fileExists(filename))
555 /* check if there is special artwork configured in level series config */
556 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
558 /* 2nd try: look for special artwork configured in level series config */
559 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
560 if (fileExists(filename))
565 /* take missing artwork configured in level set config from default */
566 skip_setup_artwork = TRUE;
570 if (!skip_setup_artwork)
572 /* 3rd try: look for special artwork in configured artwork directory */
573 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
574 if (fileExists(filename))
580 /* 4th try: look for default artwork in new default artwork directory */
581 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
582 if (fileExists(filename))
587 /* 5th try: look for default artwork in old default artwork directory */
588 filename = getPath2(options.sounds_directory, basename);
589 if (fileExists(filename))
592 return NULL; /* cannot find specified artwork file anywhere */
595 char *getCustomMusicFilename(char *basename)
597 static char *filename = NULL;
598 boolean skip_setup_artwork = FALSE;
600 if (filename != NULL)
603 basename = getCorrectedArtworkBasename(basename);
605 if (!setup.override_level_music)
607 /* 1st try: look for special artwork in current level series directory */
608 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
609 if (fileExists(filename))
614 /* check if there is special artwork configured in level series config */
615 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
617 /* 2nd try: look for special artwork configured in level series config */
618 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
619 if (fileExists(filename))
624 /* take missing artwork configured in level set config from default */
625 skip_setup_artwork = TRUE;
629 if (!skip_setup_artwork)
631 /* 3rd try: look for special artwork in configured artwork directory */
632 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
633 if (fileExists(filename))
639 /* 4th try: look for default artwork in new default artwork directory */
640 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
641 if (fileExists(filename))
646 /* 5th try: look for default artwork in old default artwork directory */
647 filename = getPath2(options.music_directory, basename);
648 if (fileExists(filename))
651 return NULL; /* cannot find specified artwork file anywhere */
654 char *getCustomArtworkFilename(char *basename, int type)
656 if (type == ARTWORK_TYPE_GRAPHICS)
657 return getCustomImageFilename(basename);
658 else if (type == ARTWORK_TYPE_SOUNDS)
659 return getCustomSoundFilename(basename);
660 else if (type == ARTWORK_TYPE_MUSIC)
661 return getCustomMusicFilename(basename);
663 return UNDEFINED_FILENAME;
666 char *getCustomArtworkConfigFilename(int type)
668 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
671 char *getCustomArtworkLevelConfigFilename(int type)
673 static char *filename = NULL;
675 if (filename != NULL)
678 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
683 char *getCustomMusicDirectory(void)
685 static char *directory = NULL;
686 boolean skip_setup_artwork = FALSE;
688 if (directory != NULL)
691 if (!setup.override_level_music)
693 /* 1st try: look for special artwork in current level series directory */
694 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
695 if (fileExists(directory))
700 /* check if there is special artwork configured in level series config */
701 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
703 /* 2nd try: look for special artwork configured in level series config */
704 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
705 if (fileExists(directory))
710 /* take missing artwork configured in level set config from default */
711 skip_setup_artwork = TRUE;
715 if (!skip_setup_artwork)
717 /* 3rd try: look for special artwork in configured artwork directory */
718 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
719 if (fileExists(directory))
725 /* 4th try: look for default artwork in new default artwork directory */
726 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
727 if (fileExists(directory))
732 /* 5th try: look for default artwork in old default artwork directory */
733 directory = getStringCopy(options.music_directory);
734 if (fileExists(directory))
737 return NULL; /* cannot find specified artwork file anywhere */
740 void InitTapeDirectory(char *level_subdir)
742 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
743 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
744 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
747 void InitScoreDirectory(char *level_subdir)
749 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
750 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
751 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
754 static void SaveUserLevelInfo();
756 void InitUserLevelDirectory(char *level_subdir)
758 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
760 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
761 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
762 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
768 void InitLevelSetupDirectory(char *level_subdir)
770 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
771 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
772 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
776 /* ------------------------------------------------------------------------- */
777 /* some functions to handle lists of level directories */
778 /* ------------------------------------------------------------------------- */
780 TreeInfo *newTreeInfo()
782 return checked_calloc(sizeof(TreeInfo));
785 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
787 node_new->next = *node_first;
788 *node_first = node_new;
791 int numTreeInfo(TreeInfo *node)
804 boolean validLevelSeries(TreeInfo *node)
806 return (node != NULL && !node->node_group && !node->parent_link);
809 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
814 if (node->node_group) /* enter level group (step down into tree) */
815 return getFirstValidTreeInfoEntry(node->node_group);
816 else if (node->parent_link) /* skip start entry of level group */
818 if (node->next) /* get first real level series entry */
819 return getFirstValidTreeInfoEntry(node->next);
820 else /* leave empty level group and go on */
821 return getFirstValidTreeInfoEntry(node->node_parent->next);
823 else /* this seems to be a regular level series */
827 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
832 if (node->node_parent == NULL) /* top level group */
833 return *node->node_top;
834 else /* sub level group */
835 return node->node_parent->node_group;
838 int numTreeInfoInGroup(TreeInfo *node)
840 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
843 int posTreeInfo(TreeInfo *node)
845 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
850 if (node_cmp == node)
854 node_cmp = node_cmp->next;
860 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
862 TreeInfo *node_default = node;
877 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
879 if (identifier == NULL)
884 if (node->node_group)
886 TreeInfo *node_group;
888 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
893 else if (!node->parent_link)
895 if (strcmp(identifier, node->identifier) == 0)
905 void dumpTreeInfo(TreeInfo *node, int depth)
909 printf("Dumping TreeInfo:\n");
913 for (i=0; i<(depth + 1) * 3; i++)
917 printf("filename == '%s' ['%s', '%s'] [%d])\n",
918 node->filename, node->fullpath, node->basepath, node->user_defined);
920 printf("filename == '%s' (%s) [%s] (%d)\n",
921 node->filename, node->name, node->identifier, node->sort_priority);
924 if (node->node_group != NULL)
925 dumpTreeInfo(node->node_group, depth + 1);
931 void sortTreeInfo(TreeInfo **node_first,
932 int (*compare_function)(const void *, const void *))
934 int num_nodes = numTreeInfo(*node_first);
935 TreeInfo **sort_array;
936 TreeInfo *node = *node_first;
942 /* allocate array for sorting structure pointers */
943 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
945 /* writing structure pointers to sorting array */
946 while (i < num_nodes && node) /* double boundary check... */
948 sort_array[i] = node;
954 /* sorting the structure pointers in the sorting array */
955 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
958 /* update the linkage of list elements with the sorted node array */
959 for (i=0; i<num_nodes - 1; i++)
960 sort_array[i]->next = sort_array[i + 1];
961 sort_array[num_nodes - 1]->next = NULL;
963 /* update the linkage of the main list anchor pointer */
964 *node_first = sort_array[0];
968 /* now recursively sort the level group structures */
972 if (node->node_group != NULL)
973 sortTreeInfo(&node->node_group, compare_function);
980 /* ========================================================================= */
981 /* some stuff from "files.c" */
982 /* ========================================================================= */
984 #if defined(PLATFORM_WIN32)
986 #define S_IRGRP S_IRUSR
989 #define S_IROTH S_IRUSR
992 #define S_IWGRP S_IWUSR
995 #define S_IWOTH S_IWUSR
998 #define S_IXGRP S_IXUSR
1001 #define S_IXOTH S_IXUSR
1004 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1009 #endif /* PLATFORM_WIN32 */
1011 /* file permissions for newly written files */
1012 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1013 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1014 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1016 #define MODE_W_PRIVATE (S_IWUSR)
1017 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1018 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1020 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1021 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1023 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1024 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1026 char *getUserDataDir(void)
1028 static char *userdata_dir = NULL;
1030 if (userdata_dir == NULL)
1031 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1033 return userdata_dir;
1036 char *getCommonDataDir(void)
1038 static char *common_data_dir = NULL;
1040 #if defined(PLATFORM_WIN32)
1041 if (common_data_dir == NULL)
1043 char *dir = checked_malloc(MAX_PATH + 1);
1045 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1046 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1047 common_data_dir = getPath2(dir, program.userdata_directory);
1049 common_data_dir = options.rw_base_directory;
1052 if (common_data_dir == NULL)
1053 common_data_dir = options.rw_base_directory;
1056 return common_data_dir;
1061 return getUserDataDir();
1064 static mode_t posix_umask(mode_t mask)
1066 #if defined(PLATFORM_UNIX)
1073 static int posix_mkdir(const char *pathname, mode_t mode)
1075 #if defined(PLATFORM_WIN32)
1076 return mkdir(pathname);
1078 return mkdir(pathname, mode);
1082 void createDirectory(char *dir, char *text, int permission_class)
1084 /* leave "other" permissions in umask untouched, but ensure group parts
1085 of USERDATA_DIR_MODE are not masked */
1086 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1087 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1088 mode_t normal_umask = posix_umask(0);
1089 mode_t group_umask = ~(dir_mode & S_IRWXG);
1090 posix_umask(normal_umask & group_umask);
1092 if (access(dir, F_OK) != 0)
1093 if (posix_mkdir(dir, dir_mode) != 0)
1094 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1096 posix_umask(normal_umask); /* reset normal umask */
1099 void InitUserDataDirectory()
1101 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1104 void SetFilePermissions(char *filename, int permission_class)
1106 chmod(filename, (permission_class == PERMS_PRIVATE ?
1107 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1110 char *getCookie(char *file_type)
1112 static char cookie[MAX_COOKIE_LEN + 1];
1114 if (strlen(program.cookie_prefix) + 1 +
1115 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1116 return "[COOKIE ERROR]"; /* should never happen */
1118 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1119 program.cookie_prefix, file_type,
1120 program.version_major, program.version_minor);
1125 int getFileVersionFromCookieString(const char *cookie)
1127 const char *ptr_cookie1, *ptr_cookie2;
1128 const char *pattern1 = "_FILE_VERSION_";
1129 const char *pattern2 = "?.?";
1130 const int len_cookie = strlen(cookie);
1131 const int len_pattern1 = strlen(pattern1);
1132 const int len_pattern2 = strlen(pattern2);
1133 const int len_pattern = len_pattern1 + len_pattern2;
1134 int version_major, version_minor;
1136 if (len_cookie <= len_pattern)
1139 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1140 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1142 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1145 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1146 ptr_cookie2[1] != '.' ||
1147 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1150 version_major = ptr_cookie2[0] - '0';
1151 version_minor = ptr_cookie2[2] - '0';
1153 return VERSION_IDENT(version_major, version_minor, 0, 0);
1156 boolean checkCookieString(const char *cookie, const char *template)
1158 const char *pattern = "_FILE_VERSION_?.?";
1159 const int len_cookie = strlen(cookie);
1160 const int len_template = strlen(template);
1161 const int len_pattern = strlen(pattern);
1163 if (len_cookie != len_template)
1166 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1172 /* ------------------------------------------------------------------------- */
1173 /* setup file list and hash handling functions */
1174 /* ------------------------------------------------------------------------- */
1176 char *getFormattedSetupEntry(char *token, char *value)
1179 static char entry[MAX_LINE_LEN];
1181 /* start with the token and some spaces to format output line */
1182 sprintf(entry, "%s:", token);
1183 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1186 /* continue with the token's value */
1187 strcat(entry, value);
1192 SetupFileList *newSetupFileList(char *token, char *value)
1194 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1196 new->token = getStringCopy(token);
1197 new->value = getStringCopy(value);
1204 void freeSetupFileList(SetupFileList *list)
1214 freeSetupFileList(list->next);
1218 char *getListEntry(SetupFileList *list, char *token)
1223 if (strcmp(list->token, token) == 0)
1226 return getListEntry(list->next, token);
1229 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1234 if (strcmp(list->token, token) == 0)
1239 list->value = getStringCopy(value);
1243 else if (list->next == NULL)
1244 return (list->next = newSetupFileList(token, value));
1246 return setListEntry(list->next, token, value);
1249 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1254 if (list->next == NULL)
1255 return (list->next = newSetupFileList(token, value));
1257 return addListEntry(list->next, token, value);
1261 static void printSetupFileList(SetupFileList *list)
1266 printf("token: '%s'\n", list->token);
1267 printf("value: '%s'\n", list->value);
1269 printSetupFileList(list->next);
1274 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1275 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1276 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1277 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1279 #define insert_hash_entry hashtable_insert
1280 #define search_hash_entry hashtable_search
1281 #define change_hash_entry hashtable_change
1282 #define remove_hash_entry hashtable_remove
1285 static unsigned int get_hash_from_key(void *key)
1290 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1291 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1292 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1293 it works better than many other constants, prime or not) has never been
1294 adequately explained.
1296 If you just want to have a good hash function, and cannot wait, djb2
1297 is one of the best string hash functions i know. It has excellent
1298 distribution and speed on many different sets of keys and table sizes.
1299 You are not likely to do better with one of the "well known" functions
1300 such as PJW, K&R, etc.
1302 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1305 char *str = (char *)key;
1306 unsigned int hash = 5381;
1309 while ((c = *str++))
1310 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1315 static int keys_are_equal(void *key1, void *key2)
1317 return (strcmp((char *)key1, (char *)key2) == 0);
1320 SetupFileHash *newSetupFileHash()
1322 SetupFileHash *new_hash =
1323 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1325 if (new_hash == NULL)
1326 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1331 void freeSetupFileHash(SetupFileHash *hash)
1336 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1339 char *getHashEntry(SetupFileHash *hash, char *token)
1344 return search_hash_entry(hash, token);
1347 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1354 value_copy = getStringCopy(value);
1356 /* change value; if it does not exist, insert it as new */
1357 if (!change_hash_entry(hash, token, value_copy))
1358 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1359 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1364 static void printSetupFileHash(SetupFileHash *hash)
1366 BEGIN_HASH_ITERATION(hash, itr)
1368 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1369 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1371 END_HASH_ITERATION(hash, itr)
1376 static void *loadSetupFileData(char *filename, boolean use_hash)
1379 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1380 char *token, *value, *line_ptr;
1381 void *setup_file_data, *insert_ptr = NULL;
1382 boolean read_continued_line = FALSE;
1386 setup_file_data = newSetupFileHash();
1388 insert_ptr = setup_file_data = newSetupFileList("", "");
1390 if (!(file = fopen(filename, MODE_READ)))
1392 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1398 /* read next line of input file */
1399 if (!fgets(line, MAX_LINE_LEN, file))
1402 /* cut trailing newline or carriage return */
1403 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1404 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1407 if (read_continued_line)
1409 /* cut leading whitespaces from input line */
1410 for (line_ptr = line; *line_ptr; line_ptr++)
1411 if (*line_ptr != ' ' && *line_ptr != '\t')
1414 /* append new line to existing line, if there is enough space */
1415 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1416 strcat(previous_line, line_ptr);
1418 strcpy(line, previous_line); /* copy storage buffer to line */
1420 read_continued_line = FALSE;
1423 /* if the last character is '\', continue at next line */
1424 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1426 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1427 strcpy(previous_line, line); /* copy line to storage buffer */
1429 read_continued_line = TRUE;
1434 /* cut trailing comment from input line */
1435 for (line_ptr = line; *line_ptr; line_ptr++)
1437 if (*line_ptr == '#')
1444 /* cut trailing whitespaces from input line */
1445 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1446 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1449 /* ignore empty lines */
1453 line_len = strlen(line);
1455 /* cut leading whitespaces from token */
1456 for (token = line; *token; token++)
1457 if (*token != ' ' && *token != '\t')
1460 /* find end of token */
1461 for (line_ptr = token; *line_ptr; line_ptr++)
1463 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1470 if (line_ptr < line + line_len)
1471 value = line_ptr + 1;
1474 value = "true"; /* treat tokens without value as "true" */
1479 /* cut leading whitespaces from value */
1480 for (; *value; value++)
1481 if (*value != ' ' && *value != '\t')
1484 if (*token && *value)
1487 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1489 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1497 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1498 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1502 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1503 SetupFileList *first_valid_list_entry = setup_file_list->next;
1505 /* free empty list header */
1506 setup_file_list->next = NULL;
1507 freeSetupFileList(setup_file_list);
1508 setup_file_data = first_valid_list_entry;
1510 if (first_valid_list_entry == NULL)
1511 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1514 return setup_file_data;
1517 SetupFileList *loadSetupFileList(char *filename)
1519 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1522 SetupFileHash *loadSetupFileHash(char *filename)
1524 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1527 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1530 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1533 Error(ERR_WARN, "configuration file has no file identifier");
1534 else if (!checkCookieString(value, identifier))
1535 Error(ERR_WARN, "configuration file has wrong file identifier");
1539 /* ========================================================================= */
1540 /* setup file stuff */
1541 /* ========================================================================= */
1543 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1544 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1545 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1547 /* level directory info */
1548 #define LEVELINFO_TOKEN_IDENTIFIER 0
1549 #define LEVELINFO_TOKEN_NAME 1
1550 #define LEVELINFO_TOKEN_NAME_SORTING 2
1551 #define LEVELINFO_TOKEN_AUTHOR 3
1552 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1553 #define LEVELINFO_TOKEN_LEVELS 5
1554 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1555 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1556 #define LEVELINFO_TOKEN_LATEST_ENGINE 8
1557 #define LEVELINFO_TOKEN_LEVEL_GROUP 9
1558 #define LEVELINFO_TOKEN_READONLY 10
1559 #define LEVELINFO_TOKEN_GRAPHICS_SET 11
1560 #define LEVELINFO_TOKEN_SOUNDS_SET 12
1561 #define LEVELINFO_TOKEN_MUSIC_SET 13
1563 #define NUM_LEVELINFO_TOKENS 14
1565 static LevelDirTree ldi;
1567 static struct TokenInfo levelinfo_tokens[] =
1569 /* level directory info */
1570 { TYPE_STRING, &ldi.identifier, "identifier" },
1571 { TYPE_STRING, &ldi.name, "name" },
1572 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1573 { TYPE_STRING, &ldi.author, "author" },
1574 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1575 { TYPE_INTEGER, &ldi.levels, "levels" },
1576 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1577 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1578 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1579 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1580 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1581 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1582 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1583 { TYPE_STRING, &ldi.music_set, "music_set" }
1586 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1590 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1591 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1592 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1593 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1596 ldi->node_parent = NULL;
1597 ldi->node_group = NULL;
1601 ldi->cl_cursor = -1;
1603 ldi->filename = NULL;
1604 ldi->fullpath = NULL;
1605 ldi->basepath = NULL;
1606 ldi->identifier = NULL;
1607 ldi->name = getStringCopy(ANONYMOUS_NAME);
1608 ldi->name_sorting = NULL;
1609 ldi->author = getStringCopy(ANONYMOUS_NAME);
1611 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1612 ldi->latest_engine = FALSE; /* default: get from level */
1613 ldi->parent_link = FALSE;
1614 ldi->user_defined = FALSE;
1616 ldi->class_desc = NULL;
1618 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1620 ldi->imported_from = NULL;
1622 ldi->graphics_set = NULL;
1623 ldi->sounds_set = NULL;
1624 ldi->music_set = NULL;
1625 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1626 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1627 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1630 ldi->first_level = 0;
1631 ldi->last_level = 0;
1632 ldi->level_group = FALSE;
1633 ldi->handicap_level = 0;
1634 ldi->readonly = TRUE;
1638 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1642 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1644 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1650 /* copy all values from the parent structure */
1652 ldi->type = parent->type;
1654 ldi->node_top = parent->node_top;
1655 ldi->node_parent = parent;
1656 ldi->node_group = NULL;
1660 ldi->cl_cursor = -1;
1662 ldi->filename = NULL;
1663 ldi->fullpath = NULL;
1664 ldi->basepath = NULL;
1665 ldi->identifier = NULL;
1666 ldi->name = getStringCopy(ANONYMOUS_NAME);
1667 ldi->name_sorting = NULL;
1668 ldi->author = getStringCopy(parent->author);
1670 ldi->sort_priority = parent->sort_priority;
1671 ldi->latest_engine = parent->latest_engine;
1672 ldi->parent_link = FALSE;
1673 ldi->user_defined = parent->user_defined;
1674 ldi->color = parent->color;
1675 ldi->class_desc = getStringCopy(parent->class_desc);
1677 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1679 ldi->imported_from = getStringCopy(parent->imported_from);
1681 ldi->graphics_set = NULL;
1682 ldi->sounds_set = NULL;
1683 ldi->music_set = NULL;
1684 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1685 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1686 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1689 ldi->first_level = 0;
1690 ldi->last_level = 0;
1691 ldi->level_group = FALSE;
1692 ldi->handicap_level = 0;
1693 ldi->readonly = TRUE;
1699 /* first copy all values from the parent structure ... */
1702 /* ... then set all fields to default that cannot be inherited from parent.
1703 This is especially important for all those fields that can be set from
1704 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1705 calls 'free()' for all already set token values which requires that no
1706 other structure's pointer may point to them!
1709 ldi->filename = NULL;
1710 ldi->fullpath = NULL;
1711 ldi->basepath = NULL;
1712 ldi->identifier = NULL;
1713 ldi->name = getStringCopy(ANONYMOUS_NAME);
1714 ldi->name_sorting = NULL;
1715 ldi->author = getStringCopy(parent->author);
1717 ldi->imported_from = getStringCopy(parent->imported_from);
1718 ldi->class_desc = getStringCopy(parent->class_desc);
1720 ldi->graphics_set = NULL;
1721 ldi->sounds_set = NULL;
1722 ldi->music_set = NULL;
1723 ldi->graphics_path = NULL;
1724 ldi->sounds_path = NULL;
1725 ldi->music_path = NULL;
1727 ldi->level_group = FALSE;
1728 ldi->parent_link = FALSE;
1730 ldi->node_top = parent->node_top;
1731 ldi->node_parent = parent;
1732 ldi->node_group = NULL;
1738 static void freeTreeInfo(TreeInfo *ldi)
1741 free(ldi->filename);
1743 free(ldi->fullpath);
1745 free(ldi->basepath);
1746 if (ldi->identifier)
1747 free(ldi->identifier);
1751 if (ldi->name_sorting)
1752 free(ldi->name_sorting);
1756 if (ldi->class_desc)
1757 free(ldi->class_desc);
1759 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1761 if (ldi->graphics_set)
1762 free(ldi->graphics_set);
1763 if (ldi->sounds_set)
1764 free(ldi->sounds_set);
1766 free(ldi->music_set);
1768 if (ldi->graphics_path)
1769 free(ldi->graphics_path);
1770 if (ldi->sounds_path)
1771 free(ldi->sounds_path);
1772 if (ldi->music_path)
1773 free(ldi->music_path);
1777 void setSetupInfo(struct TokenInfo *token_info,
1778 int token_nr, char *token_value)
1780 int token_type = token_info[token_nr].type;
1781 void *setup_value = token_info[token_nr].value;
1783 if (token_value == NULL)
1786 /* set setup field to corresponding token value */
1791 *(boolean *)setup_value = get_boolean_from_string(token_value);
1795 *(Key *)setup_value = getKeyFromKeyName(token_value);
1799 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1803 *(int *)setup_value = get_integer_from_string(token_value);
1807 if (*(char **)setup_value != NULL)
1808 free(*(char **)setup_value);
1809 *(char **)setup_value = getStringCopy(token_value);
1817 static int compareTreeInfoEntries(const void *object1, const void *object2)
1819 const TreeInfo *entry1 = *((TreeInfo **)object1);
1820 const TreeInfo *entry2 = *((TreeInfo **)object2);
1821 int class_sorting1, class_sorting2;
1824 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1826 class_sorting1 = LEVELSORTING(entry1);
1827 class_sorting2 = LEVELSORTING(entry2);
1831 class_sorting1 = ARTWORKSORTING(entry1);
1832 class_sorting2 = ARTWORKSORTING(entry2);
1835 if (entry1->parent_link || entry2->parent_link)
1836 compare_result = (entry1->parent_link ? -1 : +1);
1837 else if (entry1->sort_priority == entry2->sort_priority)
1839 char *name1 = getStringToLower(entry1->name_sorting);
1840 char *name2 = getStringToLower(entry2->name_sorting);
1842 compare_result = strcmp(name1, name2);
1847 else if (class_sorting1 == class_sorting2)
1848 compare_result = entry1->sort_priority - entry2->sort_priority;
1850 compare_result = class_sorting1 - class_sorting2;
1852 return compare_result;
1855 static void createParentTreeInfoNode(TreeInfo *node_parent)
1859 if (node_parent == NULL)
1862 ti_new = newTreeInfo();
1863 setTreeInfoToDefaults(ti_new, node_parent->type);
1865 ti_new->node_parent = node_parent;
1866 ti_new->parent_link = TRUE;
1869 setString(&ti_new->identifier, node_parent->identifier);
1870 setString(&ti_new->name, ".. (parent directory)");
1871 setString(&ti_new->name_sorting, ti_new->name);
1873 setString(&ti_new->filename, "..");
1874 setString(&ti_new->fullpath, node_parent->fullpath);
1876 ti_new->sort_priority = node_parent->sort_priority;
1877 ti_new->latest_engine = node_parent->latest_engine;
1879 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1881 ti_new->identifier = getStringCopy(node_parent->identifier);
1882 ti_new->name = ".. (parent directory)";
1883 ti_new->name_sorting = getStringCopy(ti_new->name);
1885 ti_new->filename = "..";
1886 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1888 ti_new->sort_priority = node_parent->sort_priority;
1889 ti_new->latest_engine = node_parent->latest_engine;
1891 ti_new->class_desc = getLevelClassDescription(ti_new);
1894 pushTreeInfo(&node_parent->node_group, ti_new);
1897 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1898 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1900 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1901 TreeInfo *node_parent,
1902 char *level_directory,
1903 char *directory_name)
1905 char *directory_path = getPath2(level_directory, directory_name);
1906 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1907 SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
1908 LevelDirTree *leveldir_new = NULL;
1911 if (setup_file_hash == NULL)
1913 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1915 free(directory_path);
1921 leveldir_new = newTreeInfo();
1924 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1926 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1928 leveldir_new->filename = getStringCopy(directory_name);
1930 checkSetupFileHashIdentifier(setup_file_hash, getCookie("LEVELINFO"));
1932 /* set all structure fields according to the token/value pairs */
1933 ldi = *leveldir_new;
1934 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1935 setSetupInfo(levelinfo_tokens, i,
1936 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
1937 *leveldir_new = ldi;
1940 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1941 setString(&leveldir_new->name, leveldir_new->filename);
1943 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1945 free(leveldir_new->name);
1946 leveldir_new->name = getStringCopy(leveldir_new->filename);
1950 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1952 if (leveldir_new->identifier == NULL)
1953 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1955 if (leveldir_new->name_sorting == NULL)
1956 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1958 if (node_parent == NULL) /* top level group */
1960 leveldir_new->basepath = getStringCopy(level_directory);
1961 leveldir_new->fullpath = getStringCopy(leveldir_new->filename);
1963 else /* sub level group */
1965 leveldir_new->basepath = getStringCopy(node_parent->basepath);
1966 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1969 if (leveldir_new->levels < 1)
1970 leveldir_new->levels = 1;
1972 leveldir_new->last_level =
1973 leveldir_new->first_level + leveldir_new->levels - 1;
1976 leveldir_new->user_defined =
1977 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
1979 leveldir_new->user_defined =
1980 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1983 leveldir_new->color = LEVELCOLOR(leveldir_new);
1985 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
1987 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1990 leveldir_new->handicap_level = /* set handicap to default value */
1991 (leveldir_new->user_defined ?
1992 leveldir_new->last_level :
1993 leveldir_new->first_level);
1995 pushTreeInfo(node_first, leveldir_new);
1997 freeSetupFileHash(setup_file_hash);
1999 if (leveldir_new->level_group)
2001 /* create node to link back to current level directory */
2002 createParentTreeInfoNode(leveldir_new);
2004 /* step into sub-directory and look for more level series */
2005 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2006 leveldir_new, directory_path);
2009 free(directory_path);
2015 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2016 TreeInfo *node_parent,
2017 char *level_directory)
2020 struct dirent *dir_entry;
2021 boolean valid_entry_found = FALSE;
2023 if ((dir = opendir(level_directory)) == NULL)
2025 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2029 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2031 struct stat file_status;
2032 char *directory_name = dir_entry->d_name;
2033 char *directory_path = getPath2(level_directory, directory_name);
2035 /* skip entries for current and parent directory */
2036 if (strcmp(directory_name, ".") == 0 ||
2037 strcmp(directory_name, "..") == 0)
2039 free(directory_path);
2043 /* find out if directory entry is itself a directory */
2044 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2045 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2047 free(directory_path);
2051 free(directory_path);
2053 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2054 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2055 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2058 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2065 if (!valid_entry_found)
2067 /* check if this directory directly contains a file "levelinfo.conf" */
2068 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2069 level_directory, ".");
2072 if (!valid_entry_found)
2073 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2077 void LoadLevelInfo()
2079 InitUserLevelDirectory(getLoginName());
2081 DrawInitText("Loading level series:", 120, FC_GREEN);
2083 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2084 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2086 /* before sorting, the first entries will be from the user directory */
2087 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2089 if (leveldir_first == NULL)
2090 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2092 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2095 dumpTreeInfo(leveldir_first, 0);
2099 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2100 TreeInfo *node_parent,
2101 char *base_directory,
2102 char *directory_name, int type)
2104 char *directory_path = getPath2(base_directory, directory_name);
2105 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2106 SetupFileHash *setup_file_hash = NULL;
2107 TreeInfo *artwork_new = NULL;
2110 if (access(filename, F_OK) == 0) /* file exists */
2111 setup_file_hash = loadSetupFileHash(filename);
2113 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2116 struct dirent *dir_entry;
2117 boolean valid_file_found = FALSE;
2119 if ((dir = opendir(directory_path)) != NULL)
2121 while ((dir_entry = readdir(dir)) != NULL)
2123 char *entry_name = dir_entry->d_name;
2125 if (FileIsArtworkType(entry_name, type))
2127 valid_file_found = TRUE;
2135 if (!valid_file_found)
2137 if (strcmp(directory_name, ".") != 0)
2138 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2140 free(directory_path);
2147 artwork_new = newTreeInfo();
2150 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2152 setTreeInfoToDefaults(artwork_new, type);
2154 artwork_new->filename = getStringCopy(directory_name);
2156 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2159 checkSetupFileHashIdentifier(setup_file_hash, getCookie("..."));
2162 /* set all structure fields according to the token/value pairs */
2164 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2165 setSetupInfo(levelinfo_tokens, i,
2166 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2170 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2171 setString(&artwork_new->name, artwork_new->filename);
2173 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2175 free(artwork_new->name);
2176 artwork_new->name = getStringCopy(artwork_new->filename);
2181 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2184 if (artwork_new->identifier == NULL)
2185 artwork_new->identifier = getStringCopy(artwork_new->filename);
2187 if (artwork_new->name_sorting == NULL)
2188 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2191 if (node_parent == NULL) /* top level group */
2193 artwork_new->basepath = getStringCopy(base_directory);
2194 artwork_new->fullpath = getStringCopy(artwork_new->filename);
2196 else /* sub level group */
2198 artwork_new->basepath = getStringCopy(node_parent->basepath);
2199 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2203 artwork_new->user_defined =
2204 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2206 artwork_new->user_defined =
2207 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
2210 /* (may use ".sort_priority" from "setup_file_hash" above) */
2211 artwork_new->color = ARTWORKCOLOR(artwork_new);
2213 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2215 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2218 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2221 if (artwork_new->name != NULL)
2223 free(artwork_new->name);
2224 artwork_new->name = NULL;
2229 if (artwork_new->identifier != NULL)
2231 free(artwork_new->identifier);
2232 artwork_new->identifier = NULL;
2236 if (strcmp(artwork_new->filename, ".") == 0)
2238 if (artwork_new->user_defined)
2241 setString(&artwork_new->identifier, "private");
2243 artwork_new->identifier = getStringCopy("private");
2245 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2250 setString(&artwork_new->identifier, "classic");
2252 artwork_new->identifier = getStringCopy("classic");
2254 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2257 /* set to new values after changing ".sort_priority" */
2258 artwork_new->color = ARTWORKCOLOR(artwork_new);
2260 setString(&artwork_new->class_desc,
2261 getLevelClassDescription(artwork_new));
2263 artwork_new->class_desc = getLevelClassDescription(artwork_new);
2269 setString(&artwork_new->identifier, artwork_new->filename);
2271 artwork_new->identifier = getStringCopy(artwork_new->filename);
2276 setString(&artwork_new->name, artwork_new->identifier);
2277 setString(&artwork_new->name_sorting, artwork_new->name);
2279 artwork_new->name = getStringCopy(artwork_new->identifier);
2280 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2284 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2286 pushTreeInfo(node_first, artwork_new);
2288 freeSetupFileHash(setup_file_hash);
2290 free(directory_path);
2296 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2297 TreeInfo *node_parent,
2298 char *base_directory, int type)
2301 struct dirent *dir_entry;
2302 boolean valid_entry_found = FALSE;
2304 if ((dir = opendir(base_directory)) == NULL)
2306 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2307 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2311 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2313 struct stat file_status;
2314 char *directory_name = dir_entry->d_name;
2315 char *directory_path = getPath2(base_directory, directory_name);
2317 /* skip entries for current and parent directory */
2318 if (strcmp(directory_name, ".") == 0 ||
2319 strcmp(directory_name, "..") == 0)
2321 free(directory_path);
2325 /* find out if directory entry is itself a directory */
2326 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2327 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2329 free(directory_path);
2333 free(directory_path);
2335 /* check if this directory contains artwork with or without config file */
2336 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2338 directory_name, type);
2343 /* check if this directory directly contains artwork itself */
2344 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
2345 base_directory, ".",
2347 if (!valid_entry_found)
2348 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2352 static TreeInfo *getDummyArtworkInfo(int type)
2354 /* this is only needed when there is completely no artwork available */
2355 TreeInfo *artwork_new = newTreeInfo();
2357 setTreeInfoToDefaults(artwork_new, type);
2360 setString(&artwork_new->filename, UNDEFINED_FILENAME);
2361 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2362 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2364 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2365 setString(&artwork_new->name, UNDEFINED_FILENAME);
2366 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2368 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
2369 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
2370 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
2372 if (artwork_new->name != NULL)
2373 free(artwork_new->name);
2375 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
2376 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
2377 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
2383 void LoadArtworkInfo()
2385 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2387 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2388 options.graphics_directory,
2389 TREE_TYPE_GRAPHICS_DIR);
2390 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2391 getUserGraphicsDir(),
2392 TREE_TYPE_GRAPHICS_DIR);
2394 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2395 options.sounds_directory,
2396 TREE_TYPE_SOUNDS_DIR);
2397 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2399 TREE_TYPE_SOUNDS_DIR);
2401 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2402 options.music_directory,
2403 TREE_TYPE_MUSIC_DIR);
2404 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2406 TREE_TYPE_MUSIC_DIR);
2408 if (artwork.gfx_first == NULL)
2409 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2410 if (artwork.snd_first == NULL)
2411 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2412 if (artwork.mus_first == NULL)
2413 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2415 /* before sorting, the first entries will be from the user directory */
2416 artwork.gfx_current =
2417 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2418 if (artwork.gfx_current == NULL)
2419 artwork.gfx_current =
2420 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2421 if (artwork.gfx_current == NULL)
2422 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2424 artwork.snd_current =
2425 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2426 if (artwork.snd_current == NULL)
2427 artwork.snd_current =
2428 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2429 if (artwork.snd_current == NULL)
2430 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2432 artwork.mus_current =
2433 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2434 if (artwork.mus_current == NULL)
2435 artwork.mus_current =
2436 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2437 if (artwork.mus_current == NULL)
2438 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2440 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2441 artwork.snd_current_identifier = artwork.snd_current->identifier;
2442 artwork.mus_current_identifier = artwork.mus_current->identifier;
2445 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2446 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2447 printf("music set == %s\n\n", artwork.mus_current_identifier);
2450 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2451 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2452 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2455 dumpTreeInfo(artwork.gfx_first, 0);
2456 dumpTreeInfo(artwork.snd_first, 0);
2457 dumpTreeInfo(artwork.mus_first, 0);
2461 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2462 LevelDirTree *level_node)
2464 /* recursively check all level directories for artwork sub-directories */
2468 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2469 ARTWORK_DIRECTORY((*artwork_node)->type));
2472 if (!level_node->parent_link)
2473 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
2474 level_node->filename, level_node->name);
2477 if (!level_node->parent_link)
2479 TreeInfo *topnode_last = *artwork_node;
2481 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2482 (*artwork_node)->type);
2484 if (topnode_last != *artwork_node)
2486 free((*artwork_node)->identifier);
2487 free((*artwork_node)->name);
2488 free((*artwork_node)->name_sorting);
2490 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2491 (*artwork_node)->name = getStringCopy(level_node->name);
2492 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2494 (*artwork_node)->sort_priority = level_node->sort_priority;
2495 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2501 if (level_node->node_group != NULL)
2502 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2504 level_node = level_node->next;
2508 void LoadLevelArtworkInfo()
2510 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2512 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2513 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2514 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2516 /* needed for reloading level artwork not known at ealier stage */
2518 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2520 artwork.gfx_current =
2521 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2522 if (artwork.gfx_current == NULL)
2523 artwork.gfx_current =
2524 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2525 if (artwork.gfx_current == NULL)
2526 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2529 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2531 artwork.snd_current =
2532 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2533 if (artwork.snd_current == NULL)
2534 artwork.snd_current =
2535 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2536 if (artwork.snd_current == NULL)
2537 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2540 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2542 artwork.mus_current =
2543 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2544 if (artwork.mus_current == NULL)
2545 artwork.mus_current =
2546 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2547 if (artwork.mus_current == NULL)
2548 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2551 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2552 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2553 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2556 dumpTreeInfo(artwork.gfx_first, 0);
2557 dumpTreeInfo(artwork.snd_first, 0);
2558 dumpTreeInfo(artwork.mus_first, 0);
2562 static void SaveUserLevelInfo()
2564 LevelDirTree *level_info;
2569 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2571 if (!(file = fopen(filename, MODE_WRITE)))
2573 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2578 level_info = newTreeInfo();
2580 /* always start with reliable default values */
2581 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2584 setString(&level_info->name, getLoginName());
2585 setString(&level_info->author, getRealName());
2586 level_info->levels = 100;
2587 level_info->first_level = 1;
2588 level_info->sort_priority = LEVELCLASS_PRIVATE_START;
2589 level_info->readonly = FALSE;
2590 setString(&level_info->graphics_set, GFX_CLASSIC_SUBDIR);
2591 setString(&level_info->sounds_set, SND_CLASSIC_SUBDIR);
2592 setString(&level_info->music_set, MUS_CLASSIC_SUBDIR);
2594 ldi.name = getStringCopy(getLoginName());
2595 ldi.author = getStringCopy(getRealName());
2597 ldi.first_level = 1;
2598 ldi.sort_priority = LEVELCLASS_PRIVATE_START;
2599 ldi.readonly = FALSE;
2600 ldi.graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2601 ldi.sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2602 ldi.music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2605 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2606 getCookie("LEVELINFO")));
2609 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2610 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2611 i != LEVELINFO_TOKEN_NAME_SORTING &&
2612 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2613 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2617 SetFilePermissions(filename, PERMS_PRIVATE);
2619 freeTreeInfo(level_info);
2623 char *getSetupValue(int type, void *value)
2625 static char value_string[MAX_LINE_LEN];
2633 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2637 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2641 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2645 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2649 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2653 sprintf(value_string, "%d", *(int *)value);
2657 strcpy(value_string, *(char **)value);
2661 value_string[0] = '\0';
2665 return value_string;
2668 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2672 static char token_string[MAX_LINE_LEN];
2673 int token_type = token_info[token_nr].type;
2674 void *setup_value = token_info[token_nr].value;
2675 char *token_text = token_info[token_nr].text;
2676 char *value_string = getSetupValue(token_type, setup_value);
2678 /* build complete token string */
2679 sprintf(token_string, "%s%s", prefix, token_text);
2681 /* build setup entry line */
2682 line = getFormattedSetupEntry(token_string, value_string);
2684 if (token_type == TYPE_KEY_X11)
2686 Key key = *(Key *)setup_value;
2687 char *keyname = getKeyNameFromKey(key);
2689 /* add comment, if useful */
2690 if (strcmp(keyname, "(undefined)") != 0 &&
2691 strcmp(keyname, "(unknown)") != 0)
2693 /* add at least one whitespace */
2695 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2699 strcat(line, keyname);
2706 void LoadLevelSetup_LastSeries()
2708 /* ----------------------------------------------------------------------- */
2709 /* ~/.<program>/levelsetup.conf */
2710 /* ----------------------------------------------------------------------- */
2712 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2713 SetupFileHash *level_setup_hash = NULL;
2715 /* always start with reliable default values */
2716 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2718 if ((level_setup_hash = loadSetupFileHash(filename)))
2720 char *last_level_series =
2721 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2723 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2725 if (leveldir_current == NULL)
2726 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2728 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2730 freeSetupFileHash(level_setup_hash);
2733 Error(ERR_WARN, "using default setup values");
2738 void SaveLevelSetup_LastSeries()
2740 /* ----------------------------------------------------------------------- */
2741 /* ~/.<program>/levelsetup.conf */
2742 /* ----------------------------------------------------------------------- */
2744 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2745 char *level_subdir = leveldir_current->filename;
2748 InitUserDataDirectory();
2750 if (!(file = fopen(filename, MODE_WRITE)))
2752 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2757 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2758 getCookie("LEVELSETUP")));
2759 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2764 SetFilePermissions(filename, PERMS_PRIVATE);
2769 static void checkSeriesInfo()
2771 static char *level_directory = NULL;
2773 struct dirent *dir_entry;
2775 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2777 level_directory = getPath2((leveldir_current->user_defined ?
2778 getUserLevelDir(NULL) :
2779 options.level_directory),
2780 leveldir_current->fullpath);
2782 if ((dir = opendir(level_directory)) == NULL)
2784 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2788 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2790 if (strlen(dir_entry->d_name) > 4 &&
2791 dir_entry->d_name[3] == '.' &&
2792 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2794 char levelnum_str[4];
2797 strncpy(levelnum_str, dir_entry->d_name, 3);
2798 levelnum_str[3] = '\0';
2800 levelnum_value = atoi(levelnum_str);
2803 if (levelnum_value < leveldir_current->first_level)
2805 Error(ERR_WARN, "additional level %d found", levelnum_value);
2806 leveldir_current->first_level = levelnum_value;
2808 else if (levelnum_value > leveldir_current->last_level)
2810 Error(ERR_WARN, "additional level %d found", levelnum_value);
2811 leveldir_current->last_level = levelnum_value;
2820 void LoadLevelSetup_SeriesInfo()
2823 SetupFileHash *level_setup_hash = NULL;
2824 char *level_subdir = leveldir_current->filename;
2826 /* always start with reliable default values */
2827 level_nr = leveldir_current->first_level;
2829 checkSeriesInfo(leveldir_current);
2831 /* ----------------------------------------------------------------------- */
2832 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2833 /* ----------------------------------------------------------------------- */
2835 level_subdir = leveldir_current->filename;
2837 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2839 if ((level_setup_hash = loadSetupFileHash(filename)))
2843 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2847 level_nr = atoi(token_value);
2849 if (level_nr < leveldir_current->first_level)
2850 level_nr = leveldir_current->first_level;
2851 if (level_nr > leveldir_current->last_level)
2852 level_nr = leveldir_current->last_level;
2855 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2859 int level_nr = atoi(token_value);
2861 if (level_nr < leveldir_current->first_level)
2862 level_nr = leveldir_current->first_level;
2863 if (level_nr > leveldir_current->last_level + 1)
2864 level_nr = leveldir_current->last_level;
2866 if (leveldir_current->user_defined)
2867 level_nr = leveldir_current->last_level;
2869 leveldir_current->handicap_level = level_nr;
2872 checkSetupFileHashIdentifier(level_setup_hash, getCookie("LEVELSETUP"));
2874 freeSetupFileHash(level_setup_hash);
2877 Error(ERR_WARN, "using default setup values");
2882 void SaveLevelSetup_SeriesInfo()
2885 char *level_subdir = leveldir_current->filename;
2886 char *level_nr_str = int2str(level_nr, 0);
2887 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2890 /* ----------------------------------------------------------------------- */
2891 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2892 /* ----------------------------------------------------------------------- */
2894 InitLevelSetupDirectory(level_subdir);
2896 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2898 if (!(file = fopen(filename, MODE_WRITE)))
2900 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2905 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2906 getCookie("LEVELSETUP")));
2907 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2909 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2910 handicap_level_str));
2914 SetFilePermissions(filename, PERMS_PRIVATE);