1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_YELLOW : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
49 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
50 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
53 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
54 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
55 IS_LEVELCLASS_BD(n) ? 2 : \
56 IS_LEVELCLASS_EM(n) ? 3 : \
57 IS_LEVELCLASS_SP(n) ? 4 : \
58 IS_LEVELCLASS_DX(n) ? 5 : \
59 IS_LEVELCLASS_SB(n) ? 6 : \
60 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
61 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
64 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
65 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
66 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
67 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
70 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
71 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
76 #define TOKEN_VALUE_POSITION_SHORT 32
77 #define TOKEN_VALUE_POSITION_DEFAULT 40
78 #define TOKEN_COMMENT_POSITION_DEFAULT 60
80 #define MAX_COOKIE_LEN 256
82 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
83 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
86 /* ------------------------------------------------------------------------- */
88 /* ------------------------------------------------------------------------- */
90 static char *getLevelClassDescription(TreeInfo *ldi)
92 int position = ldi->sort_priority / 100;
94 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
95 return levelclass_desc[position];
97 return "Unknown Level Class";
100 static char *getUserLevelDir(char *level_subdir)
102 static char *userlevel_dir = NULL;
103 char *data_dir = getUserDataDir();
104 char *userlevel_subdir = LEVELS_DIRECTORY;
106 checked_free(userlevel_dir);
108 if (level_subdir != NULL)
109 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
111 userlevel_dir = getPath2(data_dir, userlevel_subdir);
113 return userlevel_dir;
116 static char *getScoreDir(char *level_subdir)
118 static char *score_dir = NULL;
119 char *data_dir = getCommonDataDir();
120 char *score_subdir = SCORES_DIRECTORY;
122 checked_free(score_dir);
124 if (level_subdir != NULL)
125 score_dir = getPath3(data_dir, score_subdir, level_subdir);
127 score_dir = getPath2(data_dir, score_subdir);
132 static char *getLevelSetupDir(char *level_subdir)
134 static char *levelsetup_dir = NULL;
135 char *data_dir = getUserDataDir();
136 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
138 checked_free(levelsetup_dir);
140 if (level_subdir != NULL)
141 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
143 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
145 return levelsetup_dir;
148 static char *getLevelDirFromTreeInfo(TreeInfo *node)
150 static char *level_dir = NULL;
153 return options.level_directory;
155 checked_free(level_dir);
157 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
158 options.level_directory), node->fullpath);
163 char *getCurrentLevelDir()
165 return getLevelDirFromTreeInfo(leveldir_current);
168 static char *getTapeDir(char *level_subdir)
170 static char *tape_dir = NULL;
171 char *data_dir = getUserDataDir();
172 char *tape_subdir = TAPES_DIRECTORY;
174 checked_free(tape_dir);
176 if (level_subdir != NULL)
177 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
179 tape_dir = getPath2(data_dir, tape_subdir);
184 static char *getSolutionTapeDir()
186 static char *tape_dir = NULL;
187 char *data_dir = getCurrentLevelDir();
188 char *tape_subdir = TAPES_DIRECTORY;
190 checked_free(tape_dir);
192 tape_dir = getPath2(data_dir, tape_subdir);
197 static char *getDefaultGraphicsDir(char *graphics_subdir)
199 static char *graphics_dir = NULL;
201 if (graphics_subdir == NULL)
202 return options.graphics_directory;
204 checked_free(graphics_dir);
206 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
211 static char *getDefaultSoundsDir(char *sounds_subdir)
213 static char *sounds_dir = NULL;
215 if (sounds_subdir == NULL)
216 return options.sounds_directory;
218 checked_free(sounds_dir);
220 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
225 static char *getDefaultMusicDir(char *music_subdir)
227 static char *music_dir = NULL;
229 if (music_subdir == NULL)
230 return options.music_directory;
232 checked_free(music_dir);
234 music_dir = getPath2(options.music_directory, music_subdir);
239 static char *getDefaultArtworkSet(int type)
241 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
242 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
243 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
246 static char *getDefaultArtworkDir(int type)
248 return (type == TREE_TYPE_GRAPHICS_DIR ?
249 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
250 type == TREE_TYPE_SOUNDS_DIR ?
251 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
252 type == TREE_TYPE_MUSIC_DIR ?
253 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
256 static char *getUserGraphicsDir()
258 static char *usergraphics_dir = NULL;
260 if (usergraphics_dir == NULL)
261 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
263 return usergraphics_dir;
266 static char *getUserSoundsDir()
268 static char *usersounds_dir = NULL;
270 if (usersounds_dir == NULL)
271 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
273 return usersounds_dir;
276 static char *getUserMusicDir()
278 static char *usermusic_dir = NULL;
280 if (usermusic_dir == NULL)
281 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
283 return usermusic_dir;
286 static char *getSetupArtworkDir(TreeInfo *ti)
288 static char *artwork_dir = NULL;
290 checked_free(artwork_dir);
292 artwork_dir = getPath2(ti->basepath, ti->fullpath);
297 char *setLevelArtworkDir(TreeInfo *ti)
299 char **artwork_path_ptr, **artwork_set_ptr;
300 TreeInfo *level_artwork;
302 if (ti == NULL || leveldir_current == NULL)
305 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
306 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
308 checked_free(*artwork_path_ptr);
310 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
311 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
314 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
315 normally result in using the artwork configured in the setup menu. But
316 if an artwork subdirectory exists (which might contain custom artwork
317 or an artwork configuration file), this level artwork must be treated
318 as relative to the default "classic" artwork, not to the artwork that
319 is currently configured in the setup menu. */
321 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
323 checked_free(*artwork_set_ptr);
327 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
328 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
332 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
333 *artwork_set_ptr = NULL;
339 return *artwork_set_ptr;
342 inline static char *getLevelArtworkSet(int type)
344 if (leveldir_current == NULL)
347 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
350 inline static char *getLevelArtworkDir(int type)
352 if (leveldir_current == NULL)
353 return UNDEFINED_FILENAME;
355 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
358 char *getTapeFilename(int nr)
360 static char *filename = NULL;
361 char basename[MAX_FILENAME_LEN];
363 checked_free(filename);
365 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
366 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
371 char *getSolutionTapeFilename(int nr)
373 static char *filename = NULL;
374 char basename[MAX_FILENAME_LEN];
376 checked_free(filename);
378 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
379 filename = getPath2(getSolutionTapeDir(), basename);
384 char *getScoreFilename(int nr)
386 static char *filename = NULL;
387 char basename[MAX_FILENAME_LEN];
389 checked_free(filename);
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 checked_free(filename);
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 checked_free(filename);
413 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
415 if (fileExists(filename))
418 checked_free(filename);
419 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
424 char *getHelpAnimFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
435 char *getHelpTextFilename()
437 static char *filename = NULL;
439 checked_free(filename);
441 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
446 char *getLevelSetInfoFilename()
448 static char *filename = NULL;
463 for (i = 0; basenames[i] != NULL; i++)
465 checked_free(filename);
466 filename = getPath2(getCurrentLevelDir(), basenames[i]);
468 if (fileExists(filename))
475 static char *getCorrectedArtworkBasename(char *basename)
477 char *basename_corrected = basename;
479 #if defined(PLATFORM_MSDOS)
480 if (program.filename_prefix != NULL)
482 int prefix_len = strlen(program.filename_prefix);
484 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
485 basename_corrected = &basename[prefix_len];
487 /* if corrected filename is still longer than standard MS-DOS filename
488 size (8 characters + 1 dot + 3 characters file extension), shorten
489 filename by writing file extension after 8th basename character */
490 if (strlen(basename_corrected) > 8 + 1 + 3)
492 static char *msdos_filename = NULL;
494 checked_free(msdos_filename);
496 msdos_filename = getStringCopy(basename_corrected);
497 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
499 basename_corrected = msdos_filename;
504 return basename_corrected;
507 char *getCustomImageFilename(char *basename)
509 static char *filename = NULL;
510 boolean skip_setup_artwork = FALSE;
512 checked_free(filename);
514 basename = getCorrectedArtworkBasename(basename);
516 if (!setup.override_level_graphics)
518 /* 1st try: look for special artwork in current level series directory */
519 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
520 if (fileExists(filename))
525 /* check if there is special artwork configured in level series config */
526 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
528 /* 2nd try: look for special artwork configured in level series config */
529 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
530 if (fileExists(filename))
535 /* take missing artwork configured in level set config from default */
536 skip_setup_artwork = TRUE;
540 if (!skip_setup_artwork)
542 /* 3rd try: look for special artwork in configured artwork directory */
543 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
544 if (fileExists(filename))
550 /* 4th try: look for default artwork in new default artwork directory */
551 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
552 if (fileExists(filename))
557 /* 5th try: look for default artwork in old default artwork directory */
558 filename = getPath2(options.graphics_directory, basename);
559 if (fileExists(filename))
562 return NULL; /* cannot find specified artwork file anywhere */
565 char *getCustomSoundFilename(char *basename)
567 static char *filename = NULL;
568 boolean skip_setup_artwork = FALSE;
570 checked_free(filename);
572 basename = getCorrectedArtworkBasename(basename);
574 if (!setup.override_level_sounds)
576 /* 1st try: look for special artwork in current level series directory */
577 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
578 if (fileExists(filename))
583 /* check if there is special artwork configured in level series config */
584 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
586 /* 2nd try: look for special artwork configured in level series config */
587 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
588 if (fileExists(filename))
593 /* take missing artwork configured in level set config from default */
594 skip_setup_artwork = TRUE;
598 if (!skip_setup_artwork)
600 /* 3rd try: look for special artwork in configured artwork directory */
601 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
602 if (fileExists(filename))
608 /* 4th try: look for default artwork in new default artwork directory */
609 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
610 if (fileExists(filename))
615 /* 5th try: look for default artwork in old default artwork directory */
616 filename = getPath2(options.sounds_directory, basename);
617 if (fileExists(filename))
620 return NULL; /* cannot find specified artwork file anywhere */
623 char *getCustomMusicFilename(char *basename)
625 static char *filename = NULL;
626 boolean skip_setup_artwork = FALSE;
628 checked_free(filename);
630 basename = getCorrectedArtworkBasename(basename);
632 if (!setup.override_level_music)
634 /* 1st try: look for special artwork in current level series directory */
635 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
636 if (fileExists(filename))
641 /* check if there is special artwork configured in level series config */
642 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
644 /* 2nd try: look for special artwork configured in level series config */
645 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
646 if (fileExists(filename))
651 /* take missing artwork configured in level set config from default */
652 skip_setup_artwork = TRUE;
656 if (!skip_setup_artwork)
658 /* 3rd try: look for special artwork in configured artwork directory */
659 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
660 if (fileExists(filename))
666 /* 4th try: look for default artwork in new default artwork directory */
667 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
668 if (fileExists(filename))
673 /* 5th try: look for default artwork in old default artwork directory */
674 filename = getPath2(options.music_directory, basename);
675 if (fileExists(filename))
678 return NULL; /* cannot find specified artwork file anywhere */
681 char *getCustomArtworkFilename(char *basename, int type)
683 if (type == ARTWORK_TYPE_GRAPHICS)
684 return getCustomImageFilename(basename);
685 else if (type == ARTWORK_TYPE_SOUNDS)
686 return getCustomSoundFilename(basename);
687 else if (type == ARTWORK_TYPE_MUSIC)
688 return getCustomMusicFilename(basename);
690 return UNDEFINED_FILENAME;
693 char *getCustomArtworkConfigFilename(int type)
695 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
698 char *getCustomArtworkLevelConfigFilename(int type)
700 static char *filename = NULL;
702 checked_free(filename);
704 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
709 char *getCustomMusicDirectory(void)
711 static char *directory = NULL;
712 boolean skip_setup_artwork = FALSE;
714 checked_free(directory);
716 if (!setup.override_level_music)
718 /* 1st try: look for special artwork in current level series directory */
719 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
720 if (fileExists(directory))
725 /* check if there is special artwork configured in level series config */
726 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
728 /* 2nd try: look for special artwork configured in level series config */
729 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
730 if (fileExists(directory))
735 /* take missing artwork configured in level set config from default */
736 skip_setup_artwork = TRUE;
740 if (!skip_setup_artwork)
742 /* 3rd try: look for special artwork in configured artwork directory */
743 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
744 if (fileExists(directory))
750 /* 4th try: look for default artwork in new default artwork directory */
751 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
752 if (fileExists(directory))
757 /* 5th try: look for default artwork in old default artwork directory */
758 directory = getStringCopy(options.music_directory);
759 if (fileExists(directory))
762 return NULL; /* cannot find specified artwork file anywhere */
765 void InitTapeDirectory(char *level_subdir)
767 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
768 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
769 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
772 void InitScoreDirectory(char *level_subdir)
774 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
775 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
776 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
779 static void SaveUserLevelInfo();
781 void InitUserLevelDirectory(char *level_subdir)
783 if (!fileExists(getUserLevelDir(level_subdir)))
785 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
786 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
787 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
793 void InitLevelSetupDirectory(char *level_subdir)
795 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
796 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
797 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
801 /* ------------------------------------------------------------------------- */
802 /* some functions to handle lists of level and artwork directories */
803 /* ------------------------------------------------------------------------- */
805 TreeInfo *newTreeInfo()
807 return checked_calloc(sizeof(TreeInfo));
810 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
812 node_new->next = *node_first;
813 *node_first = node_new;
816 int numTreeInfo(TreeInfo *node)
829 boolean validLevelSeries(TreeInfo *node)
831 return (node != NULL && !node->node_group && !node->parent_link);
834 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
839 if (node->node_group) /* enter level group (step down into tree) */
840 return getFirstValidTreeInfoEntry(node->node_group);
841 else if (node->parent_link) /* skip start entry of level group */
843 if (node->next) /* get first real level series entry */
844 return getFirstValidTreeInfoEntry(node->next);
845 else /* leave empty level group and go on */
846 return getFirstValidTreeInfoEntry(node->node_parent->next);
848 else /* this seems to be a regular level series */
852 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
857 if (node->node_parent == NULL) /* top level group */
858 return *node->node_top;
859 else /* sub level group */
860 return node->node_parent->node_group;
863 int numTreeInfoInGroup(TreeInfo *node)
865 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
868 int posTreeInfo(TreeInfo *node)
870 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
875 if (node_cmp == node)
879 node_cmp = node_cmp->next;
885 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
887 TreeInfo *node_default = node;
902 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
904 if (identifier == NULL)
909 if (node->node_group)
911 TreeInfo *node_group;
913 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
918 else if (!node->parent_link)
920 if (strEqual(identifier, node->identifier))
930 TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
931 TreeInfo *node, boolean skip_sets_without_levels)
938 if (!node->parent_link && !node->level_group &&
939 skip_sets_without_levels && node->levels == 0)
940 return cloneTreeNode(node_top, node_parent, node->next,
941 skip_sets_without_levels);
943 node_new = newTreeInfo();
945 *node_new = *node; /* copy complete node */
947 node_new->node_top = node_top; /* correct top node link */
948 node_new->node_parent = node_parent; /* correct parent node link */
950 if (node->level_group)
951 node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group,
952 skip_sets_without_levels);
954 node_new->next = cloneTreeNode(node_top, node_parent, node->next,
955 skip_sets_without_levels);
960 void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
962 TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets);
967 static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
969 boolean settings_changed = FALSE;
973 if (node->graphics_set_ecs && !setup.prefer_aga_graphics &&
974 !strEqual(node->graphics_set, node->graphics_set_ecs))
976 setString(&node->graphics_set, node->graphics_set_ecs);
977 settings_changed = TRUE;
979 else if (node->graphics_set_aga && setup.prefer_aga_graphics &&
980 !strEqual(node->graphics_set, node->graphics_set_aga))
982 setString(&node->graphics_set, node->graphics_set_aga);
983 settings_changed = TRUE;
986 if (node->node_group != NULL)
987 settings_changed |= adjustTreeGraphicsForEMC(node->node_group);
992 return settings_changed;
995 void dumpTreeInfo(TreeInfo *node, int depth)
999 printf("Dumping TreeInfo:\n");
1003 for (i = 0; i < (depth + 1) * 3; i++)
1006 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
1007 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
1009 if (node->node_group != NULL)
1010 dumpTreeInfo(node->node_group, depth + 1);
1016 void sortTreeInfo(TreeInfo **node_first,
1017 int (*compare_function)(const void *, const void *))
1019 int num_nodes = numTreeInfo(*node_first);
1020 TreeInfo **sort_array;
1021 TreeInfo *node = *node_first;
1027 /* allocate array for sorting structure pointers */
1028 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1030 /* writing structure pointers to sorting array */
1031 while (i < num_nodes && node) /* double boundary check... */
1033 sort_array[i] = node;
1039 /* sorting the structure pointers in the sorting array */
1040 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1043 /* update the linkage of list elements with the sorted node array */
1044 for (i = 0; i < num_nodes - 1; i++)
1045 sort_array[i]->next = sort_array[i + 1];
1046 sort_array[num_nodes - 1]->next = NULL;
1048 /* update the linkage of the main list anchor pointer */
1049 *node_first = sort_array[0];
1053 /* now recursively sort the level group structures */
1057 if (node->node_group != NULL)
1058 sortTreeInfo(&node->node_group, compare_function);
1065 /* ========================================================================= */
1066 /* some stuff from "files.c" */
1067 /* ========================================================================= */
1069 #if defined(PLATFORM_WIN32)
1071 #define S_IRGRP S_IRUSR
1074 #define S_IROTH S_IRUSR
1077 #define S_IWGRP S_IWUSR
1080 #define S_IWOTH S_IWUSR
1083 #define S_IXGRP S_IXUSR
1086 #define S_IXOTH S_IXUSR
1089 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1094 #endif /* PLATFORM_WIN32 */
1096 /* file permissions for newly written files */
1097 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1098 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1099 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1101 #define MODE_W_PRIVATE (S_IWUSR)
1102 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1103 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1105 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1106 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1108 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1109 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1111 char *getUserDataDir(void)
1113 static char *userdata_dir = NULL;
1115 if (userdata_dir == NULL)
1116 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1118 return userdata_dir;
1121 char *getCommonDataDir(void)
1123 static char *common_data_dir = NULL;
1125 #if defined(PLATFORM_WIN32)
1126 if (common_data_dir == NULL)
1128 char *dir = checked_malloc(MAX_PATH + 1);
1130 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1131 && !strEqual(dir, "")) /* empty for Windows 95/98 */
1132 common_data_dir = getPath2(dir, program.userdata_directory);
1134 common_data_dir = options.rw_base_directory;
1137 if (common_data_dir == NULL)
1138 common_data_dir = options.rw_base_directory;
1141 return common_data_dir;
1146 return getUserDataDir();
1149 static mode_t posix_umask(mode_t mask)
1151 #if defined(PLATFORM_UNIX)
1158 static int posix_mkdir(const char *pathname, mode_t mode)
1160 #if defined(PLATFORM_WIN32)
1161 return mkdir(pathname);
1163 return mkdir(pathname, mode);
1167 void createDirectory(char *dir, char *text, int permission_class)
1169 /* leave "other" permissions in umask untouched, but ensure group parts
1170 of USERDATA_DIR_MODE are not masked */
1171 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1172 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1173 mode_t normal_umask = posix_umask(0);
1174 mode_t group_umask = ~(dir_mode & S_IRWXG);
1175 posix_umask(normal_umask & group_umask);
1177 if (!fileExists(dir))
1178 if (posix_mkdir(dir, dir_mode) != 0)
1179 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1181 posix_umask(normal_umask); /* reset normal umask */
1184 void InitUserDataDirectory()
1186 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1189 void SetFilePermissions(char *filename, int permission_class)
1191 chmod(filename, (permission_class == PERMS_PRIVATE ?
1192 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1195 char *getCookie(char *file_type)
1197 static char cookie[MAX_COOKIE_LEN + 1];
1199 if (strlen(program.cookie_prefix) + 1 +
1200 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1201 return "[COOKIE ERROR]"; /* should never happen */
1203 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1204 program.cookie_prefix, file_type,
1205 program.version_major, program.version_minor);
1210 int getFileVersionFromCookieString(const char *cookie)
1212 const char *ptr_cookie1, *ptr_cookie2;
1213 const char *pattern1 = "_FILE_VERSION_";
1214 const char *pattern2 = "?.?";
1215 const int len_cookie = strlen(cookie);
1216 const int len_pattern1 = strlen(pattern1);
1217 const int len_pattern2 = strlen(pattern2);
1218 const int len_pattern = len_pattern1 + len_pattern2;
1219 int version_major, version_minor;
1221 if (len_cookie <= len_pattern)
1224 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1225 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1227 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1230 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1231 ptr_cookie2[1] != '.' ||
1232 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1235 version_major = ptr_cookie2[0] - '0';
1236 version_minor = ptr_cookie2[2] - '0';
1238 return VERSION_IDENT(version_major, version_minor, 0, 0);
1241 boolean checkCookieString(const char *cookie, const char *template)
1243 const char *pattern = "_FILE_VERSION_?.?";
1244 const int len_cookie = strlen(cookie);
1245 const int len_template = strlen(template);
1246 const int len_pattern = strlen(pattern);
1248 if (len_cookie != len_template)
1251 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1257 /* ------------------------------------------------------------------------- */
1258 /* setup file list and hash handling functions */
1259 /* ------------------------------------------------------------------------- */
1261 char *getFormattedSetupEntry(char *token, char *value)
1264 static char entry[MAX_LINE_LEN];
1266 /* if value is an empty string, just return token without value */
1270 /* start with the token and some spaces to format output line */
1271 sprintf(entry, "%s:", token);
1272 for (i = strlen(entry); i < token_value_position; i++)
1275 /* continue with the token's value */
1276 strcat(entry, value);
1281 SetupFileList *newSetupFileList(char *token, char *value)
1283 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1285 new->token = getStringCopy(token);
1286 new->value = getStringCopy(value);
1293 void freeSetupFileList(SetupFileList *list)
1298 checked_free(list->token);
1299 checked_free(list->value);
1302 freeSetupFileList(list->next);
1307 char *getListEntry(SetupFileList *list, char *token)
1312 if (strEqual(list->token, token))
1315 return getListEntry(list->next, token);
1318 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1323 if (strEqual(list->token, token))
1325 checked_free(list->value);
1327 list->value = getStringCopy(value);
1331 else if (list->next == NULL)
1332 return (list->next = newSetupFileList(token, value));
1334 return setListEntry(list->next, token, value);
1337 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1342 if (list->next == NULL)
1343 return (list->next = newSetupFileList(token, value));
1345 return addListEntry(list->next, token, value);
1349 static void printSetupFileList(SetupFileList *list)
1354 printf("token: '%s'\n", list->token);
1355 printf("value: '%s'\n", list->value);
1357 printSetupFileList(list->next);
1362 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1363 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1364 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1365 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1367 #define insert_hash_entry hashtable_insert
1368 #define search_hash_entry hashtable_search
1369 #define change_hash_entry hashtable_change
1370 #define remove_hash_entry hashtable_remove
1373 static unsigned int get_hash_from_key(void *key)
1378 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1379 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1380 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1381 it works better than many other constants, prime or not) has never been
1382 adequately explained.
1384 If you just want to have a good hash function, and cannot wait, djb2
1385 is one of the best string hash functions i know. It has excellent
1386 distribution and speed on many different sets of keys and table sizes.
1387 You are not likely to do better with one of the "well known" functions
1388 such as PJW, K&R, etc.
1390 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1393 char *str = (char *)key;
1394 unsigned int hash = 5381;
1397 while ((c = *str++))
1398 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1403 static int keys_are_equal(void *key1, void *key2)
1405 return (strEqual((char *)key1, (char *)key2));
1408 SetupFileHash *newSetupFileHash()
1410 SetupFileHash *new_hash =
1411 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1413 if (new_hash == NULL)
1414 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1419 void freeSetupFileHash(SetupFileHash *hash)
1424 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1427 char *getHashEntry(SetupFileHash *hash, char *token)
1432 return search_hash_entry(hash, token);
1435 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1442 value_copy = getStringCopy(value);
1444 /* change value; if it does not exist, insert it as new */
1445 if (!change_hash_entry(hash, token, value_copy))
1446 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1447 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1450 char *removeHashEntry(SetupFileHash *hash, char *token)
1455 return remove_hash_entry(hash, token);
1459 static void printSetupFileHash(SetupFileHash *hash)
1461 BEGIN_HASH_ITERATION(hash, itr)
1463 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1464 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1466 END_HASH_ITERATION(hash, itr)
1470 static void *loadSetupFileData(char *filename, boolean use_hash)
1472 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1473 char *token, *value, *line_ptr;
1474 void *setup_file_data, *insert_ptr = NULL;
1475 boolean read_continued_line = FALSE;
1478 if (!(file = fopen(filename, MODE_READ)))
1480 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1486 setup_file_data = newSetupFileHash();
1488 insert_ptr = setup_file_data = newSetupFileList("", "");
1492 /* read next line of input file */
1493 if (!fgets(line, MAX_LINE_LEN, file))
1496 /* cut trailing newline or carriage return */
1497 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1498 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1501 if (read_continued_line)
1503 /* cut leading whitespaces from input line */
1504 for (line_ptr = line; *line_ptr; line_ptr++)
1505 if (*line_ptr != ' ' && *line_ptr != '\t')
1508 /* append new line to existing line, if there is enough space */
1509 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1510 strcat(previous_line, line_ptr);
1512 strcpy(line, previous_line); /* copy storage buffer to line */
1514 read_continued_line = FALSE;
1517 /* if the last character is '\', continue at next line */
1518 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1520 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1521 strcpy(previous_line, line); /* copy line to storage buffer */
1523 read_continued_line = TRUE;
1528 /* cut trailing comment from input line */
1529 for (line_ptr = line; *line_ptr; line_ptr++)
1531 if (*line_ptr == '#')
1538 /* cut trailing whitespaces from input line */
1539 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1540 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1543 /* ignore empty lines */
1547 /* cut leading whitespaces from token */
1548 for (token = line; *token; token++)
1549 if (*token != ' ' && *token != '\t')
1552 /* start with empty value as reliable default */
1555 /* find end of token to determine start of value */
1556 for (line_ptr = token; *line_ptr; line_ptr++)
1558 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1560 *line_ptr = '\0'; /* terminate token string */
1561 value = line_ptr + 1; /* set beginning of value */
1567 /* cut leading whitespaces from value */
1568 for (; *value; value++)
1569 if (*value != ' ' && *value != '\t')
1574 value = "true"; /* treat tokens without value as "true" */
1580 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1582 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1590 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1591 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1595 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1596 SetupFileList *first_valid_list_entry = setup_file_list->next;
1598 /* free empty list header */
1599 setup_file_list->next = NULL;
1600 freeSetupFileList(setup_file_list);
1601 setup_file_data = first_valid_list_entry;
1603 if (first_valid_list_entry == NULL)
1604 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1607 return setup_file_data;
1610 SetupFileList *loadSetupFileList(char *filename)
1612 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1615 SetupFileHash *loadSetupFileHash(char *filename)
1617 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1620 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1621 char *filename, char *identifier)
1623 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1626 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1627 else if (!checkCookieString(value, identifier))
1628 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1632 /* ========================================================================= */
1633 /* setup file stuff */
1634 /* ========================================================================= */
1636 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1637 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1638 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1640 /* level directory info */
1641 #define LEVELINFO_TOKEN_IDENTIFIER 0
1642 #define LEVELINFO_TOKEN_NAME 1
1643 #define LEVELINFO_TOKEN_NAME_SORTING 2
1644 #define LEVELINFO_TOKEN_AUTHOR 3
1645 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1646 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1647 #define LEVELINFO_TOKEN_LEVELS 6
1648 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1649 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1650 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1651 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1652 #define LEVELINFO_TOKEN_READONLY 11
1653 #define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 12
1654 #define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 13
1655 #define LEVELINFO_TOKEN_GRAPHICS_SET 14
1656 #define LEVELINFO_TOKEN_SOUNDS_SET 15
1657 #define LEVELINFO_TOKEN_MUSIC_SET 16
1658 #define LEVELINFO_TOKEN_FILENAME 17
1659 #define LEVELINFO_TOKEN_FILETYPE 18
1660 #define LEVELINFO_TOKEN_HANDICAP 19
1661 #define LEVELINFO_TOKEN_SKIP_LEVELS 20
1663 #define NUM_LEVELINFO_TOKENS 21
1665 static LevelDirTree ldi;
1667 static struct TokenInfo levelinfo_tokens[] =
1669 /* level directory info */
1670 { TYPE_STRING, &ldi.identifier, "identifier" },
1671 { TYPE_STRING, &ldi.name, "name" },
1672 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1673 { TYPE_STRING, &ldi.author, "author" },
1674 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1675 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1676 { TYPE_INTEGER, &ldi.levels, "levels" },
1677 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1678 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1679 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1680 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1681 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1682 { TYPE_STRING, &ldi.graphics_set_ecs, "graphics_set.ecs" },
1683 { TYPE_STRING, &ldi.graphics_set_aga, "graphics_set.aga" },
1684 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1685 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1686 { TYPE_STRING, &ldi.music_set, "music_set" },
1687 { TYPE_STRING, &ldi.level_filename, "filename" },
1688 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1689 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1690 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1693 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1697 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1698 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1699 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1700 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1703 ldi->node_parent = NULL;
1704 ldi->node_group = NULL;
1708 ldi->cl_cursor = -1;
1711 ldi->fullpath = NULL;
1712 ldi->basepath = NULL;
1713 ldi->identifier = NULL;
1714 ldi->name = getStringCopy(ANONYMOUS_NAME);
1715 ldi->name_sorting = NULL;
1716 ldi->author = getStringCopy(ANONYMOUS_NAME);
1718 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1719 ldi->latest_engine = FALSE; /* default: get from level */
1720 ldi->parent_link = FALSE;
1721 ldi->in_user_dir = FALSE;
1722 ldi->user_defined = FALSE;
1724 ldi->class_desc = NULL;
1726 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1728 ldi->imported_from = NULL;
1729 ldi->imported_by = NULL;
1731 ldi->graphics_set_ecs = NULL;
1732 ldi->graphics_set_aga = NULL;
1733 ldi->graphics_set = NULL;
1734 ldi->sounds_set = NULL;
1735 ldi->music_set = NULL;
1736 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1737 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1738 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1740 ldi->level_filename = NULL;
1741 ldi->level_filetype = NULL;
1744 ldi->first_level = 0;
1745 ldi->last_level = 0;
1746 ldi->level_group = FALSE;
1747 ldi->handicap_level = 0;
1748 ldi->readonly = TRUE;
1749 ldi->handicap = TRUE;
1750 ldi->skip_levels = FALSE;
1754 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1758 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1760 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1765 /* copy all values from the parent structure */
1767 ldi->type = parent->type;
1769 ldi->node_top = parent->node_top;
1770 ldi->node_parent = parent;
1771 ldi->node_group = NULL;
1775 ldi->cl_cursor = -1;
1778 ldi->fullpath = NULL;
1779 ldi->basepath = NULL;
1780 ldi->identifier = NULL;
1781 ldi->name = getStringCopy(ANONYMOUS_NAME);
1782 ldi->name_sorting = NULL;
1783 ldi->author = getStringCopy(parent->author);
1785 ldi->sort_priority = parent->sort_priority;
1786 ldi->latest_engine = parent->latest_engine;
1787 ldi->parent_link = FALSE;
1788 ldi->in_user_dir = parent->in_user_dir;
1789 ldi->user_defined = parent->user_defined;
1790 ldi->color = parent->color;
1791 ldi->class_desc = getStringCopy(parent->class_desc);
1793 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1795 ldi->imported_from = getStringCopy(parent->imported_from);
1796 ldi->imported_by = getStringCopy(parent->imported_by);
1798 ldi->graphics_set_ecs = NULL;
1799 ldi->graphics_set_aga = NULL;
1800 ldi->graphics_set = NULL;
1801 ldi->sounds_set = NULL;
1802 ldi->music_set = NULL;
1803 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1804 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1805 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1807 ldi->level_filename = NULL;
1808 ldi->level_filetype = NULL;
1811 ldi->first_level = 0;
1812 ldi->last_level = 0;
1813 ldi->level_group = FALSE;
1814 ldi->handicap_level = 0;
1815 ldi->readonly = TRUE;
1816 ldi->handicap = TRUE;
1817 ldi->skip_levels = FALSE;
1821 static void freeTreeInfo(TreeInfo *ldi)
1823 checked_free(ldi->subdir);
1824 checked_free(ldi->fullpath);
1825 checked_free(ldi->basepath);
1826 checked_free(ldi->identifier);
1828 checked_free(ldi->name);
1829 checked_free(ldi->name_sorting);
1830 checked_free(ldi->author);
1832 checked_free(ldi->class_desc);
1834 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1836 checked_free(ldi->imported_from);
1837 checked_free(ldi->imported_by);
1839 checked_free(ldi->graphics_set_ecs);
1840 checked_free(ldi->graphics_set_aga);
1841 checked_free(ldi->graphics_set);
1842 checked_free(ldi->sounds_set);
1843 checked_free(ldi->music_set);
1845 checked_free(ldi->graphics_path);
1846 checked_free(ldi->sounds_path);
1847 checked_free(ldi->music_path);
1849 checked_free(ldi->level_filename);
1850 checked_free(ldi->level_filetype);
1854 void setSetupInfo(struct TokenInfo *token_info,
1855 int token_nr, char *token_value)
1857 int token_type = token_info[token_nr].type;
1858 void *setup_value = token_info[token_nr].value;
1860 if (token_value == NULL)
1863 /* set setup field to corresponding token value */
1868 *(boolean *)setup_value = get_boolean_from_string(token_value);
1872 *(Key *)setup_value = getKeyFromKeyName(token_value);
1876 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1880 *(int *)setup_value = get_integer_from_string(token_value);
1884 checked_free(*(char **)setup_value);
1885 *(char **)setup_value = getStringCopy(token_value);
1893 static int compareTreeInfoEntries(const void *object1, const void *object2)
1895 const TreeInfo *entry1 = *((TreeInfo **)object1);
1896 const TreeInfo *entry2 = *((TreeInfo **)object2);
1897 int class_sorting1, class_sorting2;
1900 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1902 class_sorting1 = LEVELSORTING(entry1);
1903 class_sorting2 = LEVELSORTING(entry2);
1907 class_sorting1 = ARTWORKSORTING(entry1);
1908 class_sorting2 = ARTWORKSORTING(entry2);
1911 if (entry1->parent_link || entry2->parent_link)
1912 compare_result = (entry1->parent_link ? -1 : +1);
1913 else if (entry1->sort_priority == entry2->sort_priority)
1915 char *name1 = getStringToLower(entry1->name_sorting);
1916 char *name2 = getStringToLower(entry2->name_sorting);
1918 compare_result = strcmp(name1, name2);
1923 else if (class_sorting1 == class_sorting2)
1924 compare_result = entry1->sort_priority - entry2->sort_priority;
1926 compare_result = class_sorting1 - class_sorting2;
1928 return compare_result;
1931 static void createParentTreeInfoNode(TreeInfo *node_parent)
1935 if (node_parent == NULL)
1938 ti_new = newTreeInfo();
1939 setTreeInfoToDefaults(ti_new, node_parent->type);
1941 ti_new->node_parent = node_parent;
1942 ti_new->parent_link = TRUE;
1944 setString(&ti_new->identifier, node_parent->identifier);
1945 setString(&ti_new->name, ".. (parent directory)");
1946 setString(&ti_new->name_sorting, ti_new->name);
1948 setString(&ti_new->subdir, "..");
1949 setString(&ti_new->fullpath, node_parent->fullpath);
1951 ti_new->sort_priority = node_parent->sort_priority;
1952 ti_new->latest_engine = node_parent->latest_engine;
1954 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
1956 pushTreeInfo(&node_parent->node_group, ti_new);
1959 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1960 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1962 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1963 TreeInfo *node_parent,
1964 char *level_directory,
1965 char *directory_name)
1967 char *directory_path = getPath2(level_directory, directory_name);
1968 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1969 SetupFileHash *setup_file_hash;
1970 LevelDirTree *leveldir_new = NULL;
1973 /* unless debugging, silently ignore directories without "levelinfo.conf" */
1974 if (!options.debug && !fileExists(filename))
1976 free(directory_path);
1982 setup_file_hash = loadSetupFileHash(filename);
1984 if (setup_file_hash == NULL)
1986 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1988 free(directory_path);
1994 leveldir_new = newTreeInfo();
1997 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1999 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
2001 leveldir_new->subdir = getStringCopy(directory_name);
2003 checkSetupFileHashIdentifier(setup_file_hash, filename,
2004 getCookie("LEVELINFO"));
2006 /* set all structure fields according to the token/value pairs */
2007 ldi = *leveldir_new;
2008 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2009 setSetupInfo(levelinfo_tokens, i,
2010 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2011 *leveldir_new = ldi;
2013 if (strEqual(leveldir_new->name, ANONYMOUS_NAME))
2014 setString(&leveldir_new->name, leveldir_new->subdir);
2016 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2018 if (leveldir_new->identifier == NULL)
2019 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2021 if (leveldir_new->name_sorting == NULL)
2022 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2024 if (node_parent == NULL) /* top level group */
2026 leveldir_new->basepath = getStringCopy(level_directory);
2027 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2029 else /* sub level group */
2031 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2032 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2036 if (leveldir_new->levels < 1)
2037 leveldir_new->levels = 1;
2040 leveldir_new->last_level =
2041 leveldir_new->first_level + leveldir_new->levels - 1;
2043 leveldir_new->in_user_dir =
2044 (!strEqual(leveldir_new->basepath, options.level_directory));
2046 /* adjust some settings if user's private level directory was detected */
2047 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2048 leveldir_new->in_user_dir &&
2049 (strEqual(leveldir_new->subdir, getLoginName()) ||
2050 strEqual(leveldir_new->name, getLoginName()) ||
2051 strEqual(leveldir_new->author, getRealName())))
2053 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2054 leveldir_new->readonly = FALSE;
2057 leveldir_new->user_defined =
2058 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2060 leveldir_new->color = LEVELCOLOR(leveldir_new);
2062 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2064 leveldir_new->handicap_level = /* set handicap to default value */
2065 (leveldir_new->user_defined || !leveldir_new->handicap ?
2066 leveldir_new->last_level : leveldir_new->first_level);
2069 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2071 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2073 /* skip level sets without levels (which are probably artwork base sets) */
2075 freeSetupFileHash(setup_file_hash);
2076 free(directory_path);
2084 pushTreeInfo(node_first, leveldir_new);
2086 freeSetupFileHash(setup_file_hash);
2088 if (leveldir_new->level_group)
2090 /* create node to link back to current level directory */
2091 createParentTreeInfoNode(leveldir_new);
2093 /* step into sub-directory and look for more level series */
2094 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2095 leveldir_new, directory_path);
2098 free(directory_path);
2104 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2105 TreeInfo *node_parent,
2106 char *level_directory)
2109 struct dirent *dir_entry;
2110 boolean valid_entry_found = FALSE;
2112 if ((dir = opendir(level_directory)) == NULL)
2114 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2118 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2120 struct stat file_status;
2121 char *directory_name = dir_entry->d_name;
2122 char *directory_path = getPath2(level_directory, directory_name);
2124 /* skip entries for current and parent directory */
2125 if (strEqual(directory_name, ".") ||
2126 strEqual(directory_name, ".."))
2128 free(directory_path);
2132 /* find out if directory entry is itself a directory */
2133 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2134 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2136 free(directory_path);
2140 free(directory_path);
2142 if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
2143 strEqual(directory_name, SOUNDS_DIRECTORY) ||
2144 strEqual(directory_name, MUSIC_DIRECTORY))
2147 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2154 /* special case: top level directory may directly contain "levelinfo.conf" */
2155 if (node_parent == NULL && !valid_entry_found)
2157 /* check if this directory directly contains a file "levelinfo.conf" */
2158 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2159 level_directory, ".");
2162 if (!valid_entry_found)
2163 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2167 boolean AdjustGraphicsForEMC()
2169 boolean settings_changed = FALSE;
2171 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all);
2172 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first);
2174 return settings_changed;
2177 void LoadLevelInfo()
2179 InitUserLevelDirectory(getLoginName());
2181 DrawInitText("Loading level series:", 120, FC_GREEN);
2183 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2184 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2187 /* after loading all level set information, clone the level directory tree
2188 and remove all level sets without levels (these may still contain artwork
2189 to be offered in the setup menu as "custom artwork", and are therefore
2190 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2191 leveldir_first_all = leveldir_first;
2192 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2195 AdjustGraphicsForEMC();
2197 /* before sorting, the first entries will be from the user directory */
2198 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2200 if (leveldir_first == NULL)
2201 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2203 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2206 dumpTreeInfo(leveldir_first, 0);
2210 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2211 TreeInfo *node_parent,
2212 char *base_directory,
2213 char *directory_name, int type)
2215 char *directory_path = getPath2(base_directory, directory_name);
2216 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2217 SetupFileHash *setup_file_hash = NULL;
2218 TreeInfo *artwork_new = NULL;
2221 if (fileExists(filename))
2222 setup_file_hash = loadSetupFileHash(filename);
2224 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2227 struct dirent *dir_entry;
2228 boolean valid_file_found = FALSE;
2230 if ((dir = opendir(directory_path)) != NULL)
2232 while ((dir_entry = readdir(dir)) != NULL)
2234 char *entry_name = dir_entry->d_name;
2236 if (FileIsArtworkType(entry_name, type))
2238 valid_file_found = TRUE;
2246 if (!valid_file_found)
2248 if (!strEqual(directory_name, "."))
2249 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2251 free(directory_path);
2258 artwork_new = newTreeInfo();
2261 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2263 setTreeInfoToDefaults(artwork_new, type);
2265 artwork_new->subdir = getStringCopy(directory_name);
2267 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2270 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2273 /* set all structure fields according to the token/value pairs */
2275 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2276 setSetupInfo(levelinfo_tokens, i,
2277 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2280 if (strEqual(artwork_new->name, ANONYMOUS_NAME))
2281 setString(&artwork_new->name, artwork_new->subdir);
2284 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2287 if (artwork_new->identifier == NULL)
2288 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2290 if (artwork_new->name_sorting == NULL)
2291 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2294 if (node_parent == NULL) /* top level group */
2296 artwork_new->basepath = getStringCopy(base_directory);
2297 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2299 else /* sub level group */
2301 artwork_new->basepath = getStringCopy(node_parent->basepath);
2302 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2305 artwork_new->in_user_dir =
2306 (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)));
2308 /* (may use ".sort_priority" from "setup_file_hash" above) */
2309 artwork_new->color = ARTWORKCOLOR(artwork_new);
2311 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2313 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2315 if (strEqual(artwork_new->subdir, "."))
2317 if (artwork_new->user_defined)
2319 setString(&artwork_new->identifier, "private");
2320 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2324 setString(&artwork_new->identifier, "classic");
2325 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2328 /* set to new values after changing ".sort_priority" */
2329 artwork_new->color = ARTWORKCOLOR(artwork_new);
2331 setString(&artwork_new->class_desc,
2332 getLevelClassDescription(artwork_new));
2336 setString(&artwork_new->identifier, artwork_new->subdir);
2339 setString(&artwork_new->name, artwork_new->identifier);
2340 setString(&artwork_new->name_sorting, artwork_new->name);
2343 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2345 pushTreeInfo(node_first, artwork_new);
2347 freeSetupFileHash(setup_file_hash);
2349 free(directory_path);
2355 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2356 TreeInfo *node_parent,
2357 char *base_directory, int type)
2360 struct dirent *dir_entry;
2361 boolean valid_entry_found = FALSE;
2363 if ((dir = opendir(base_directory)) == NULL)
2365 /* display error if directory is main "options.graphics_directory" etc. */
2366 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2367 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2372 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2374 struct stat file_status;
2375 char *directory_name = dir_entry->d_name;
2376 char *directory_path = getPath2(base_directory, directory_name);
2378 /* skip directory entries for current and parent directory */
2379 if (strEqual(directory_name, ".") ||
2380 strEqual(directory_name, ".."))
2382 free(directory_path);
2386 /* skip directory entries which are not a directory or are not accessible */
2387 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2388 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2390 free(directory_path);
2394 free(directory_path);
2396 /* check if this directory contains artwork with or without config file */
2397 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2399 directory_name, type);
2404 /* check if this directory directly contains artwork itself */
2405 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2406 base_directory, ".",
2408 if (!valid_entry_found)
2409 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2413 static TreeInfo *getDummyArtworkInfo(int type)
2415 /* this is only needed when there is completely no artwork available */
2416 TreeInfo *artwork_new = newTreeInfo();
2418 setTreeInfoToDefaults(artwork_new, type);
2420 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2421 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2422 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2424 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2425 setString(&artwork_new->name, UNDEFINED_FILENAME);
2426 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2431 void LoadArtworkInfo()
2433 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2435 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2436 options.graphics_directory,
2437 TREE_TYPE_GRAPHICS_DIR);
2438 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2439 getUserGraphicsDir(),
2440 TREE_TYPE_GRAPHICS_DIR);
2442 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2443 options.sounds_directory,
2444 TREE_TYPE_SOUNDS_DIR);
2445 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2447 TREE_TYPE_SOUNDS_DIR);
2449 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2450 options.music_directory,
2451 TREE_TYPE_MUSIC_DIR);
2452 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2454 TREE_TYPE_MUSIC_DIR);
2456 if (artwork.gfx_first == NULL)
2457 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2458 if (artwork.snd_first == NULL)
2459 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2460 if (artwork.mus_first == NULL)
2461 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2463 /* before sorting, the first entries will be from the user directory */
2464 artwork.gfx_current =
2465 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2466 if (artwork.gfx_current == NULL)
2467 artwork.gfx_current =
2468 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2469 if (artwork.gfx_current == NULL)
2470 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2472 artwork.snd_current =
2473 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2474 if (artwork.snd_current == NULL)
2475 artwork.snd_current =
2476 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2477 if (artwork.snd_current == NULL)
2478 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2480 artwork.mus_current =
2481 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2482 if (artwork.mus_current == NULL)
2483 artwork.mus_current =
2484 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2485 if (artwork.mus_current == NULL)
2486 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2488 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2489 artwork.snd_current_identifier = artwork.snd_current->identifier;
2490 artwork.mus_current_identifier = artwork.mus_current->identifier;
2493 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2494 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2495 printf("music set == %s\n\n", artwork.mus_current_identifier);
2498 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2499 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2500 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2503 dumpTreeInfo(artwork.gfx_first, 0);
2504 dumpTreeInfo(artwork.snd_first, 0);
2505 dumpTreeInfo(artwork.mus_first, 0);
2509 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2510 LevelDirTree *level_node)
2512 /* recursively check all level directories for artwork sub-directories */
2516 /* check all tree entries for artwork, but skip parent link entries */
2517 if (!level_node->parent_link)
2519 TreeInfo *topnode_last = *artwork_node;
2520 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2521 ARTWORK_DIRECTORY((*artwork_node)->type));
2523 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2524 (*artwork_node)->type);
2526 if (topnode_last != *artwork_node)
2528 free((*artwork_node)->identifier);
2529 free((*artwork_node)->name);
2530 free((*artwork_node)->name_sorting);
2532 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2533 (*artwork_node)->name = getStringCopy(level_node->name);
2534 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2536 (*artwork_node)->sort_priority = level_node->sort_priority;
2537 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2543 if (level_node->node_group != NULL)
2544 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2546 level_node = level_node->next;
2550 void LoadLevelArtworkInfo()
2552 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2554 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2555 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2556 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2558 /* needed for reloading level artwork not known at ealier stage */
2560 if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set))
2562 artwork.gfx_current =
2563 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2564 if (artwork.gfx_current == NULL)
2565 artwork.gfx_current =
2566 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2567 if (artwork.gfx_current == NULL)
2568 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2571 if (!strEqual(artwork.snd_current_identifier, setup.sounds_set))
2573 artwork.snd_current =
2574 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2575 if (artwork.snd_current == NULL)
2576 artwork.snd_current =
2577 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2578 if (artwork.snd_current == NULL)
2579 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2582 if (!strEqual(artwork.mus_current_identifier, setup.music_set))
2584 artwork.mus_current =
2585 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2586 if (artwork.mus_current == NULL)
2587 artwork.mus_current =
2588 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2589 if (artwork.mus_current == NULL)
2590 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2593 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2594 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2595 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2598 dumpTreeInfo(artwork.gfx_first, 0);
2599 dumpTreeInfo(artwork.snd_first, 0);
2600 dumpTreeInfo(artwork.mus_first, 0);
2604 static void SaveUserLevelInfo()
2606 LevelDirTree *level_info;
2611 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2613 if (!(file = fopen(filename, MODE_WRITE)))
2615 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2620 level_info = newTreeInfo();
2622 /* always start with reliable default values */
2623 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2625 setString(&level_info->name, getLoginName());
2626 setString(&level_info->author, getRealName());
2627 level_info->levels = 100;
2628 level_info->first_level = 1;
2630 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2632 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2633 getCookie("LEVELINFO")));
2636 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2638 if (i == LEVELINFO_TOKEN_NAME ||
2639 i == LEVELINFO_TOKEN_AUTHOR ||
2640 i == LEVELINFO_TOKEN_LEVELS ||
2641 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2642 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2644 /* just to make things nicer :) */
2645 if (i == LEVELINFO_TOKEN_AUTHOR)
2646 fprintf(file, "\n");
2649 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2653 SetFilePermissions(filename, PERMS_PRIVATE);
2655 freeTreeInfo(level_info);
2659 char *getSetupValue(int type, void *value)
2661 static char value_string[MAX_LINE_LEN];
2669 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2673 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2677 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2681 strcpy(value_string, (*(boolean *)value ? "AGA" : "ECS"));
2685 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2689 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2693 sprintf(value_string, "%d", *(int *)value);
2697 strcpy(value_string, *(char **)value);
2701 value_string[0] = '\0';
2705 if (type & TYPE_GHOSTED)
2706 strcpy(value_string, "n/a");
2708 return value_string;
2711 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2715 static char token_string[MAX_LINE_LEN];
2716 int token_type = token_info[token_nr].type;
2717 void *setup_value = token_info[token_nr].value;
2718 char *token_text = token_info[token_nr].text;
2719 char *value_string = getSetupValue(token_type, setup_value);
2721 /* build complete token string */
2722 sprintf(token_string, "%s%s", prefix, token_text);
2724 /* build setup entry line */
2725 line = getFormattedSetupEntry(token_string, value_string);
2727 if (token_type == TYPE_KEY_X11)
2729 Key key = *(Key *)setup_value;
2730 char *keyname = getKeyNameFromKey(key);
2732 /* add comment, if useful */
2733 if (!strEqual(keyname, "(undefined)") &&
2734 !strEqual(keyname, "(unknown)"))
2736 /* add at least one whitespace */
2738 for (i = strlen(line); i < token_comment_position; i++)
2742 strcat(line, keyname);
2749 void LoadLevelSetup_LastSeries()
2751 /* ----------------------------------------------------------------------- */
2752 /* ~/.<program>/levelsetup.conf */
2753 /* ----------------------------------------------------------------------- */
2755 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2756 SetupFileHash *level_setup_hash = NULL;
2758 /* always start with reliable default values */
2759 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2761 if ((level_setup_hash = loadSetupFileHash(filename)))
2763 char *last_level_series =
2764 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2766 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2768 if (leveldir_current == NULL)
2769 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2771 checkSetupFileHashIdentifier(level_setup_hash, filename,
2772 getCookie("LEVELSETUP"));
2774 freeSetupFileHash(level_setup_hash);
2777 Error(ERR_WARN, "using default setup values");
2782 void SaveLevelSetup_LastSeries()
2784 /* ----------------------------------------------------------------------- */
2785 /* ~/.<program>/levelsetup.conf */
2786 /* ----------------------------------------------------------------------- */
2788 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2789 char *level_subdir = leveldir_current->subdir;
2792 InitUserDataDirectory();
2794 if (!(file = fopen(filename, MODE_WRITE)))
2796 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2801 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2802 getCookie("LEVELSETUP")));
2803 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2808 SetFilePermissions(filename, PERMS_PRIVATE);
2813 static void checkSeriesInfo()
2815 static char *level_directory = NULL;
2817 struct dirent *dir_entry;
2819 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2821 level_directory = getPath2((leveldir_current->in_user_dir ?
2822 getUserLevelDir(NULL) :
2823 options.level_directory),
2824 leveldir_current->fullpath);
2826 if ((dir = opendir(level_directory)) == NULL)
2828 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2832 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2834 if (strlen(dir_entry->d_name) > 4 &&
2835 dir_entry->d_name[3] == '.' &&
2836 strEqual(&dir_entry->d_name[4], LEVELFILE_EXTENSION))
2838 char levelnum_str[4];
2841 strncpy(levelnum_str, dir_entry->d_name, 3);
2842 levelnum_str[3] = '\0';
2844 levelnum_value = atoi(levelnum_str);
2847 if (levelnum_value < leveldir_current->first_level)
2849 Error(ERR_WARN, "additional level %d found", levelnum_value);
2850 leveldir_current->first_level = levelnum_value;
2852 else if (levelnum_value > leveldir_current->last_level)
2854 Error(ERR_WARN, "additional level %d found", levelnum_value);
2855 leveldir_current->last_level = levelnum_value;
2864 void LoadLevelSetup_SeriesInfo()
2867 SetupFileHash *level_setup_hash = NULL;
2868 char *level_subdir = leveldir_current->subdir;
2870 /* always start with reliable default values */
2871 level_nr = leveldir_current->first_level;
2873 checkSeriesInfo(leveldir_current);
2875 /* ----------------------------------------------------------------------- */
2876 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2877 /* ----------------------------------------------------------------------- */
2879 level_subdir = leveldir_current->subdir;
2881 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2883 if ((level_setup_hash = loadSetupFileHash(filename)))
2887 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2891 level_nr = atoi(token_value);
2893 if (level_nr < leveldir_current->first_level)
2894 level_nr = leveldir_current->first_level;
2895 if (level_nr > leveldir_current->last_level)
2896 level_nr = leveldir_current->last_level;
2899 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2903 int level_nr = atoi(token_value);
2905 if (level_nr < leveldir_current->first_level)
2906 level_nr = leveldir_current->first_level;
2907 if (level_nr > leveldir_current->last_level + 1)
2908 level_nr = leveldir_current->last_level;
2910 if (leveldir_current->user_defined || !leveldir_current->handicap)
2911 level_nr = leveldir_current->last_level;
2913 leveldir_current->handicap_level = level_nr;
2916 checkSetupFileHashIdentifier(level_setup_hash, filename,
2917 getCookie("LEVELSETUP"));
2919 freeSetupFileHash(level_setup_hash);
2922 Error(ERR_WARN, "using default setup values");
2927 void SaveLevelSetup_SeriesInfo()
2930 char *level_subdir = leveldir_current->subdir;
2931 char *level_nr_str = int2str(level_nr, 0);
2932 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2935 /* ----------------------------------------------------------------------- */
2936 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2937 /* ----------------------------------------------------------------------- */
2939 InitLevelSetupDirectory(level_subdir);
2941 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2943 if (!(file = fopen(filename, MODE_WRITE)))
2945 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2950 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2951 getCookie("LEVELSETUP")));
2952 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2954 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2955 handicap_level_str));
2959 SetFilePermissions(filename, PERMS_PRIVATE);