1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
22 #if !defined(PLATFORM_WIN32)
24 #include <sys/param.h>
34 #define NUM_LEVELCLASS_DESC 8
36 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
49 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
50 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
51 IS_LEVELCLASS_BD(n) ? FC_YELLOW : \
52 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
53 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
54 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
55 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
56 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
57 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
60 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
61 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
62 IS_LEVELCLASS_BD(n) ? 2 : \
63 IS_LEVELCLASS_EM(n) ? 3 : \
64 IS_LEVELCLASS_SP(n) ? 4 : \
65 IS_LEVELCLASS_DX(n) ? 5 : \
66 IS_LEVELCLASS_SB(n) ? 6 : \
67 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
68 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
71 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
74 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
77 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
78 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
79 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
80 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
83 #define TOKEN_VALUE_POSITION_SHORT 32
84 #define TOKEN_VALUE_POSITION_DEFAULT 40
85 #define TOKEN_COMMENT_POSITION_DEFAULT 60
87 #define MAX_COOKIE_LEN 256
89 static void setTreeInfoToDefaults(TreeInfo *, int);
90 static int compareTreeInfoEntries(const void *, const void *);
92 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
93 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
96 /* ------------------------------------------------------------------------- */
98 /* ------------------------------------------------------------------------- */
100 static char *getLevelClassDescription(TreeInfo *ti)
102 int position = ti->sort_priority / 100;
104 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
105 return levelclass_desc[position];
107 return "Unknown Level Class";
110 static char *getUserLevelDir(char *level_subdir)
112 static char *userlevel_dir = NULL;
113 char *data_dir = getUserGameDataDir();
114 char *userlevel_subdir = LEVELS_DIRECTORY;
116 checked_free(userlevel_dir);
118 if (level_subdir != NULL)
119 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
121 userlevel_dir = getPath2(data_dir, userlevel_subdir);
123 return userlevel_dir;
126 static char *getScoreDir(char *level_subdir)
128 static char *score_dir = NULL;
129 char *data_dir = getCommonDataDir();
130 char *score_subdir = SCORES_DIRECTORY;
132 checked_free(score_dir);
134 if (level_subdir != NULL)
135 score_dir = getPath3(data_dir, score_subdir, level_subdir);
137 score_dir = getPath2(data_dir, score_subdir);
142 static char *getLevelSetupDir(char *level_subdir)
144 static char *levelsetup_dir = NULL;
145 char *data_dir = getUserGameDataDir();
146 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
148 checked_free(levelsetup_dir);
150 if (level_subdir != NULL)
151 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
153 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
155 return levelsetup_dir;
158 static char *getLevelDirFromTreeInfo(TreeInfo *node)
160 static char *level_dir = NULL;
163 return options.level_directory;
165 checked_free(level_dir);
167 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
168 options.level_directory), node->fullpath);
173 char *getCurrentLevelDir()
175 return getLevelDirFromTreeInfo(leveldir_current);
178 static char *getTapeDir(char *level_subdir)
180 static char *tape_dir = NULL;
181 char *data_dir = getUserGameDataDir();
182 char *tape_subdir = TAPES_DIRECTORY;
184 checked_free(tape_dir);
186 if (level_subdir != NULL)
187 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
189 tape_dir = getPath2(data_dir, tape_subdir);
194 static char *getSolutionTapeDir()
196 static char *tape_dir = NULL;
197 char *data_dir = getCurrentLevelDir();
198 char *tape_subdir = TAPES_DIRECTORY;
200 checked_free(tape_dir);
202 tape_dir = getPath2(data_dir, tape_subdir);
207 static char *getDefaultGraphicsDir(char *graphics_subdir)
209 static char *graphics_dir = NULL;
211 if (graphics_subdir == NULL)
212 return options.graphics_directory;
214 checked_free(graphics_dir);
216 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
221 static char *getDefaultSoundsDir(char *sounds_subdir)
223 static char *sounds_dir = NULL;
225 if (sounds_subdir == NULL)
226 return options.sounds_directory;
228 checked_free(sounds_dir);
230 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
235 static char *getDefaultMusicDir(char *music_subdir)
237 static char *music_dir = NULL;
239 if (music_subdir == NULL)
240 return options.music_directory;
242 checked_free(music_dir);
244 music_dir = getPath2(options.music_directory, music_subdir);
249 static char *getDefaultArtworkSet(int type)
251 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
252 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
253 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
256 static char *getDefaultArtworkDir(int type)
258 return (type == TREE_TYPE_GRAPHICS_DIR ?
259 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
260 type == TREE_TYPE_SOUNDS_DIR ?
261 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
262 type == TREE_TYPE_MUSIC_DIR ?
263 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
266 static char *getUserGraphicsDir()
268 static char *usergraphics_dir = NULL;
270 if (usergraphics_dir == NULL)
271 usergraphics_dir = getPath2(getUserGameDataDir(), GRAPHICS_DIRECTORY);
273 return usergraphics_dir;
276 static char *getUserSoundsDir()
278 static char *usersounds_dir = NULL;
280 if (usersounds_dir == NULL)
281 usersounds_dir = getPath2(getUserGameDataDir(), SOUNDS_DIRECTORY);
283 return usersounds_dir;
286 static char *getUserMusicDir()
288 static char *usermusic_dir = NULL;
290 if (usermusic_dir == NULL)
291 usermusic_dir = getPath2(getUserGameDataDir(), MUSIC_DIRECTORY);
293 return usermusic_dir;
296 static char *getSetupArtworkDir(TreeInfo *ti)
298 static char *artwork_dir = NULL;
300 checked_free(artwork_dir);
302 artwork_dir = getPath2(ti->basepath, ti->fullpath);
307 char *setLevelArtworkDir(TreeInfo *ti)
309 char **artwork_path_ptr, **artwork_set_ptr;
310 TreeInfo *level_artwork;
312 if (ti == NULL || leveldir_current == NULL)
315 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
316 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
318 checked_free(*artwork_path_ptr);
320 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
321 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
324 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
325 normally result in using the artwork configured in the setup menu. But
326 if an artwork subdirectory exists (which might contain custom artwork
327 or an artwork configuration file), this level artwork must be treated
328 as relative to the default "classic" artwork, not to the artwork that
329 is currently configured in the setup menu. */
331 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
333 checked_free(*artwork_set_ptr);
337 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
338 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
342 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
343 *artwork_set_ptr = NULL;
349 return *artwork_set_ptr;
352 inline static char *getLevelArtworkSet(int type)
354 if (leveldir_current == NULL)
357 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
360 inline static char *getLevelArtworkDir(int type)
362 if (leveldir_current == NULL)
363 return UNDEFINED_FILENAME;
365 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
368 char *getTapeFilename(int nr)
370 static char *filename = NULL;
371 char basename[MAX_FILENAME_LEN];
373 checked_free(filename);
375 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
376 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
381 char *getSolutionTapeFilename(int nr)
383 static char *filename = NULL;
384 char basename[MAX_FILENAME_LEN];
386 checked_free(filename);
388 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
389 filename = getPath2(getSolutionTapeDir(), basename);
394 char *getScoreFilename(int nr)
396 static char *filename = NULL;
397 char basename[MAX_FILENAME_LEN];
399 checked_free(filename);
401 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
402 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
407 char *getSetupFilename()
409 static char *filename = NULL;
411 checked_free(filename);
413 filename = getPath2(getSetupDir(), SETUP_FILENAME);
418 char *getEditorSetupFilename()
420 static char *filename = NULL;
422 checked_free(filename);
423 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
425 if (fileExists(filename))
428 checked_free(filename);
429 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
434 char *getHelpAnimFilename()
436 static char *filename = NULL;
438 checked_free(filename);
440 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
445 char *getHelpTextFilename()
447 static char *filename = NULL;
449 checked_free(filename);
451 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
456 char *getLevelSetInfoFilename()
458 static char *filename = NULL;
473 for (i = 0; basenames[i] != NULL; i++)
475 checked_free(filename);
476 filename = getPath2(getCurrentLevelDir(), basenames[i]);
478 if (fileExists(filename))
485 static char *getCorrectedArtworkBasename(char *basename)
487 char *basename_corrected = basename;
489 #if defined(PLATFORM_MSDOS)
490 if (program.filename_prefix != NULL)
492 int prefix_len = strlen(program.filename_prefix);
494 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
495 basename_corrected = &basename[prefix_len];
497 /* if corrected filename is still longer than standard MS-DOS filename
498 size (8 characters + 1 dot + 3 characters file extension), shorten
499 filename by writing file extension after 8th basename character */
500 if (strlen(basename_corrected) > 8 + 1 + 3)
502 static char *msdos_filename = NULL;
504 checked_free(msdos_filename);
506 msdos_filename = getStringCopy(basename_corrected);
507 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
509 basename_corrected = msdos_filename;
514 return basename_corrected;
517 char *getCustomImageFilename(char *basename)
519 static char *filename = NULL;
520 boolean skip_setup_artwork = FALSE;
522 checked_free(filename);
524 basename = getCorrectedArtworkBasename(basename);
526 if (!setup.override_level_graphics)
528 /* 1st try: look for special artwork in current level series directory */
529 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
530 if (fileExists(filename))
535 /* check if there is special artwork configured in level series config */
536 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
538 /* 2nd try: look for special artwork configured in level series config */
539 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
540 if (fileExists(filename))
545 /* take missing artwork configured in level set config from default */
546 skip_setup_artwork = TRUE;
550 if (!skip_setup_artwork)
552 /* 3rd try: look for special artwork in configured artwork directory */
553 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
554 if (fileExists(filename))
560 /* 4th try: look for default artwork in new default artwork directory */
561 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
562 if (fileExists(filename))
567 /* 5th try: look for default artwork in old default artwork directory */
568 filename = getPath2(options.graphics_directory, basename);
569 if (fileExists(filename))
572 return NULL; /* cannot find specified artwork file anywhere */
575 char *getCustomSoundFilename(char *basename)
577 static char *filename = NULL;
578 boolean skip_setup_artwork = FALSE;
580 checked_free(filename);
582 basename = getCorrectedArtworkBasename(basename);
584 if (!setup.override_level_sounds)
586 /* 1st try: look for special artwork in current level series directory */
587 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
588 if (fileExists(filename))
593 /* check if there is special artwork configured in level series config */
594 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
596 /* 2nd try: look for special artwork configured in level series config */
597 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
598 if (fileExists(filename))
603 /* take missing artwork configured in level set config from default */
604 skip_setup_artwork = TRUE;
608 if (!skip_setup_artwork)
610 /* 3rd try: look for special artwork in configured artwork directory */
611 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
612 if (fileExists(filename))
618 /* 4th try: look for default artwork in new default artwork directory */
619 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
620 if (fileExists(filename))
625 /* 5th try: look for default artwork in old default artwork directory */
626 filename = getPath2(options.sounds_directory, basename);
627 if (fileExists(filename))
630 return NULL; /* cannot find specified artwork file anywhere */
633 char *getCustomMusicFilename(char *basename)
635 static char *filename = NULL;
636 boolean skip_setup_artwork = FALSE;
638 checked_free(filename);
640 basename = getCorrectedArtworkBasename(basename);
642 if (!setup.override_level_music)
644 /* 1st try: look for special artwork in current level series directory */
645 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
646 if (fileExists(filename))
651 /* check if there is special artwork configured in level series config */
652 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
654 /* 2nd try: look for special artwork configured in level series config */
655 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
656 if (fileExists(filename))
661 /* take missing artwork configured in level set config from default */
662 skip_setup_artwork = TRUE;
666 if (!skip_setup_artwork)
668 /* 3rd try: look for special artwork in configured artwork directory */
669 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
670 if (fileExists(filename))
676 /* 4th try: look for default artwork in new default artwork directory */
677 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
678 if (fileExists(filename))
683 /* 5th try: look for default artwork in old default artwork directory */
684 filename = getPath2(options.music_directory, basename);
685 if (fileExists(filename))
688 return NULL; /* cannot find specified artwork file anywhere */
691 char *getCustomArtworkFilename(char *basename, int type)
693 if (type == ARTWORK_TYPE_GRAPHICS)
694 return getCustomImageFilename(basename);
695 else if (type == ARTWORK_TYPE_SOUNDS)
696 return getCustomSoundFilename(basename);
697 else if (type == ARTWORK_TYPE_MUSIC)
698 return getCustomMusicFilename(basename);
700 return UNDEFINED_FILENAME;
703 char *getCustomArtworkConfigFilename(int type)
705 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
708 char *getCustomArtworkLevelConfigFilename(int type)
710 static char *filename = NULL;
712 checked_free(filename);
714 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
719 char *getCustomMusicDirectory(void)
721 static char *directory = NULL;
722 boolean skip_setup_artwork = FALSE;
724 checked_free(directory);
726 if (!setup.override_level_music)
728 /* 1st try: look for special artwork in current level series directory */
729 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
730 if (fileExists(directory))
735 /* check if there is special artwork configured in level series config */
736 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
738 /* 2nd try: look for special artwork configured in level series config */
739 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
740 if (fileExists(directory))
745 /* take missing artwork configured in level set config from default */
746 skip_setup_artwork = TRUE;
750 if (!skip_setup_artwork)
752 /* 3rd try: look for special artwork in configured artwork directory */
753 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
754 if (fileExists(directory))
760 /* 4th try: look for default artwork in new default artwork directory */
761 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
762 if (fileExists(directory))
767 /* 5th try: look for default artwork in old default artwork directory */
768 directory = getStringCopy(options.music_directory);
769 if (fileExists(directory))
772 return NULL; /* cannot find specified artwork file anywhere */
775 void InitTapeDirectory(char *level_subdir)
777 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
778 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
779 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
782 void InitScoreDirectory(char *level_subdir)
784 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
785 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
786 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
789 static void SaveUserLevelInfo();
791 void InitUserLevelDirectory(char *level_subdir)
793 if (!fileExists(getUserLevelDir(level_subdir)))
795 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
796 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
797 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
803 void InitLevelSetupDirectory(char *level_subdir)
805 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
806 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
807 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
811 /* ------------------------------------------------------------------------- */
812 /* some functions to handle lists of level and artwork directories */
813 /* ------------------------------------------------------------------------- */
815 TreeInfo *newTreeInfo()
817 return checked_calloc(sizeof(TreeInfo));
820 TreeInfo *newTreeInfo_setDefaults(int type)
822 TreeInfo *ti = newTreeInfo();
824 setTreeInfoToDefaults(ti, type);
829 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
831 node_new->next = *node_first;
832 *node_first = node_new;
835 int numTreeInfo(TreeInfo *node)
848 boolean validLevelSeries(TreeInfo *node)
850 return (node != NULL && !node->node_group && !node->parent_link);
853 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
858 if (node->node_group) /* enter level group (step down into tree) */
859 return getFirstValidTreeInfoEntry(node->node_group);
860 else if (node->parent_link) /* skip start entry of level group */
862 if (node->next) /* get first real level series entry */
863 return getFirstValidTreeInfoEntry(node->next);
864 else /* leave empty level group and go on */
865 return getFirstValidTreeInfoEntry(node->node_parent->next);
867 else /* this seems to be a regular level series */
871 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
876 if (node->node_parent == NULL) /* top level group */
877 return *node->node_top;
878 else /* sub level group */
879 return node->node_parent->node_group;
882 int numTreeInfoInGroup(TreeInfo *node)
884 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
887 int posTreeInfo(TreeInfo *node)
889 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
894 if (node_cmp == node)
898 node_cmp = node_cmp->next;
904 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
906 TreeInfo *node_default = node;
921 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
923 if (identifier == NULL)
928 if (node->node_group)
930 TreeInfo *node_group;
932 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
937 else if (!node->parent_link)
939 if (strEqual(identifier, node->identifier))
949 TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
950 TreeInfo *node, boolean skip_sets_without_levels)
957 if (!node->parent_link && !node->level_group &&
958 skip_sets_without_levels && node->levels == 0)
959 return cloneTreeNode(node_top, node_parent, node->next,
960 skip_sets_without_levels);
962 node_new = newTreeInfo();
964 *node_new = *node; /* copy complete node */
966 node_new->node_top = node_top; /* correct top node link */
967 node_new->node_parent = node_parent; /* correct parent node link */
969 if (node->level_group)
970 node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group,
971 skip_sets_without_levels);
973 node_new->next = cloneTreeNode(node_top, node_parent, node->next,
974 skip_sets_without_levels);
979 void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
981 TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets);
986 static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
988 boolean settings_changed = FALSE;
992 if (node->graphics_set_ecs && !setup.prefer_aga_graphics &&
993 !strEqual(node->graphics_set, node->graphics_set_ecs))
995 setString(&node->graphics_set, node->graphics_set_ecs);
996 settings_changed = TRUE;
998 else if (node->graphics_set_aga && setup.prefer_aga_graphics &&
999 !strEqual(node->graphics_set, node->graphics_set_aga))
1001 setString(&node->graphics_set, node->graphics_set_aga);
1002 settings_changed = TRUE;
1005 if (node->node_group != NULL)
1006 settings_changed |= adjustTreeGraphicsForEMC(node->node_group);
1011 return settings_changed;
1014 void dumpTreeInfo(TreeInfo *node, int depth)
1018 printf("Dumping TreeInfo:\n");
1022 for (i = 0; i < (depth + 1) * 3; i++)
1025 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
1026 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
1028 if (node->node_group != NULL)
1029 dumpTreeInfo(node->node_group, depth + 1);
1035 void sortTreeInfoBySortFunction(TreeInfo **node_first,
1036 int (*compare_function)(const void *,
1039 int num_nodes = numTreeInfo(*node_first);
1040 TreeInfo **sort_array;
1041 TreeInfo *node = *node_first;
1047 /* allocate array for sorting structure pointers */
1048 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1050 /* writing structure pointers to sorting array */
1051 while (i < num_nodes && node) /* double boundary check... */
1053 sort_array[i] = node;
1059 /* sorting the structure pointers in the sorting array */
1060 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1063 /* update the linkage of list elements with the sorted node array */
1064 for (i = 0; i < num_nodes - 1; i++)
1065 sort_array[i]->next = sort_array[i + 1];
1066 sort_array[num_nodes - 1]->next = NULL;
1068 /* update the linkage of the main list anchor pointer */
1069 *node_first = sort_array[0];
1073 /* now recursively sort the level group structures */
1077 if (node->node_group != NULL)
1078 sortTreeInfoBySortFunction(&node->node_group, compare_function);
1084 void sortTreeInfo(TreeInfo **node_first)
1086 sortTreeInfoBySortFunction(node_first, compareTreeInfoEntries);
1090 /* ========================================================================= */
1091 /* some stuff from "files.c" */
1092 /* ========================================================================= */
1094 #if defined(PLATFORM_WIN32)
1096 #define S_IRGRP S_IRUSR
1099 #define S_IROTH S_IRUSR
1102 #define S_IWGRP S_IWUSR
1105 #define S_IWOTH S_IWUSR
1108 #define S_IXGRP S_IXUSR
1111 #define S_IXOTH S_IXUSR
1114 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1119 #endif /* PLATFORM_WIN32 */
1121 /* file permissions for newly written files */
1122 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1123 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1124 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1126 #define MODE_W_PRIVATE (S_IWUSR)
1127 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1128 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1130 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1131 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1133 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1134 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1138 static char *dir = NULL;
1140 #if defined(PLATFORM_WIN32)
1143 dir = checked_malloc(MAX_PATH + 1);
1145 if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir)))
1148 #elif defined(PLATFORM_UNIX)
1151 if ((dir = getenv("HOME")) == NULL)
1155 if ((pwd = getpwuid(getuid())) != NULL)
1156 dir = getStringCopy(pwd->pw_dir);
1168 char *getCommonDataDir(void)
1170 static char *common_data_dir = NULL;
1172 #if defined(PLATFORM_WIN32)
1173 if (common_data_dir == NULL)
1175 char *dir = checked_malloc(MAX_PATH + 1);
1177 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1178 && !strEqual(dir, "")) /* empty for Windows 95/98 */
1179 common_data_dir = getPath2(dir, program.userdata_subdir);
1181 common_data_dir = options.rw_base_directory;
1184 if (common_data_dir == NULL)
1185 common_data_dir = options.rw_base_directory;
1188 return common_data_dir;
1191 char *getPersonalDataDir(void)
1193 static char *personal_data_dir = NULL;
1195 #if defined(PLATFORM_MACOSX)
1196 if (personal_data_dir == NULL)
1197 personal_data_dir = getPath2(getHomeDir(), "Documents");
1199 if (personal_data_dir == NULL)
1200 personal_data_dir = getHomeDir();
1203 return personal_data_dir;
1206 char *getUserGameDataDir(void)
1208 static char *user_game_data_dir = NULL;
1210 if (user_game_data_dir == NULL)
1211 user_game_data_dir = getPath2(getPersonalDataDir(),
1212 program.userdata_subdir);
1214 return user_game_data_dir;
1217 void updateUserGameDataDir()
1219 #if defined(PLATFORM_MACOSX)
1220 char *userdata_dir_old = getPath2(getHomeDir(), program.userdata_subdir_unix);
1221 char *userdata_dir_new = getUserGameDataDir(); /* do not free() this */
1223 /* convert old Unix style game data directory to Mac OS X style, if needed */
1224 if (fileExists(userdata_dir_old) && !fileExists(userdata_dir_new))
1226 if (rename(userdata_dir_old, userdata_dir_new) != 0)
1228 Error(ERR_WARN, "cannot move game data directory '%s' to '%s'",
1229 userdata_dir_old, userdata_dir_new);
1231 /* continue using Unix style data directory -- this should not happen */
1232 program.userdata_path = getPath2(getPersonalDataDir(),
1233 program.userdata_subdir_unix);
1237 free(userdata_dir_old);
1243 return getUserGameDataDir();
1246 static mode_t posix_umask(mode_t mask)
1248 #if defined(PLATFORM_UNIX)
1255 static int posix_mkdir(const char *pathname, mode_t mode)
1257 #if defined(PLATFORM_WIN32)
1258 return mkdir(pathname);
1260 return mkdir(pathname, mode);
1264 void createDirectory(char *dir, char *text, int permission_class)
1266 /* leave "other" permissions in umask untouched, but ensure group parts
1267 of USERDATA_DIR_MODE are not masked */
1268 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1269 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1270 mode_t normal_umask = posix_umask(0);
1271 mode_t group_umask = ~(dir_mode & S_IRWXG);
1272 posix_umask(normal_umask & group_umask);
1274 if (!fileExists(dir))
1275 if (posix_mkdir(dir, dir_mode) != 0)
1276 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1278 posix_umask(normal_umask); /* reset normal umask */
1281 void InitUserDataDirectory()
1283 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
1286 void SetFilePermissions(char *filename, int permission_class)
1288 chmod(filename, (permission_class == PERMS_PRIVATE ?
1289 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1292 char *getCookie(char *file_type)
1294 static char cookie[MAX_COOKIE_LEN + 1];
1296 if (strlen(program.cookie_prefix) + 1 +
1297 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1298 return "[COOKIE ERROR]"; /* should never happen */
1300 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1301 program.cookie_prefix, file_type,
1302 program.version_major, program.version_minor);
1307 int getFileVersionFromCookieString(const char *cookie)
1309 const char *ptr_cookie1, *ptr_cookie2;
1310 const char *pattern1 = "_FILE_VERSION_";
1311 const char *pattern2 = "?.?";
1312 const int len_cookie = strlen(cookie);
1313 const int len_pattern1 = strlen(pattern1);
1314 const int len_pattern2 = strlen(pattern2);
1315 const int len_pattern = len_pattern1 + len_pattern2;
1316 int version_major, version_minor;
1318 if (len_cookie <= len_pattern)
1321 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1322 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1324 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1327 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1328 ptr_cookie2[1] != '.' ||
1329 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1332 version_major = ptr_cookie2[0] - '0';
1333 version_minor = ptr_cookie2[2] - '0';
1335 return VERSION_IDENT(version_major, version_minor, 0, 0);
1338 boolean checkCookieString(const char *cookie, const char *template)
1340 const char *pattern = "_FILE_VERSION_?.?";
1341 const int len_cookie = strlen(cookie);
1342 const int len_template = strlen(template);
1343 const int len_pattern = strlen(pattern);
1345 if (len_cookie != len_template)
1348 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1354 /* ------------------------------------------------------------------------- */
1355 /* setup file list and hash handling functions */
1356 /* ------------------------------------------------------------------------- */
1358 char *getFormattedSetupEntry(char *token, char *value)
1361 static char entry[MAX_LINE_LEN];
1363 /* if value is an empty string, just return token without value */
1367 /* start with the token and some spaces to format output line */
1368 sprintf(entry, "%s:", token);
1369 for (i = strlen(entry); i < token_value_position; i++)
1372 /* continue with the token's value */
1373 strcat(entry, value);
1378 SetupFileList *newSetupFileList(char *token, char *value)
1380 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1382 new->token = getStringCopy(token);
1383 new->value = getStringCopy(value);
1390 void freeSetupFileList(SetupFileList *list)
1395 checked_free(list->token);
1396 checked_free(list->value);
1399 freeSetupFileList(list->next);
1404 char *getListEntry(SetupFileList *list, char *token)
1409 if (strEqual(list->token, token))
1412 return getListEntry(list->next, token);
1415 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1420 if (strEqual(list->token, token))
1422 checked_free(list->value);
1424 list->value = getStringCopy(value);
1428 else if (list->next == NULL)
1429 return (list->next = newSetupFileList(token, value));
1431 return setListEntry(list->next, token, value);
1434 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1439 if (list->next == NULL)
1440 return (list->next = newSetupFileList(token, value));
1442 return addListEntry(list->next, token, value);
1446 static void printSetupFileList(SetupFileList *list)
1451 printf("token: '%s'\n", list->token);
1452 printf("value: '%s'\n", list->value);
1454 printSetupFileList(list->next);
1459 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1460 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1461 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1462 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1464 #define insert_hash_entry hashtable_insert
1465 #define search_hash_entry hashtable_search
1466 #define change_hash_entry hashtable_change
1467 #define remove_hash_entry hashtable_remove
1470 static unsigned int get_hash_from_key(void *key)
1475 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1476 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1477 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1478 it works better than many other constants, prime or not) has never been
1479 adequately explained.
1481 If you just want to have a good hash function, and cannot wait, djb2
1482 is one of the best string hash functions i know. It has excellent
1483 distribution and speed on many different sets of keys and table sizes.
1484 You are not likely to do better with one of the "well known" functions
1485 such as PJW, K&R, etc.
1487 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1490 char *str = (char *)key;
1491 unsigned int hash = 5381;
1494 while ((c = *str++))
1495 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1500 static int keys_are_equal(void *key1, void *key2)
1502 return (strEqual((char *)key1, (char *)key2));
1505 SetupFileHash *newSetupFileHash()
1507 SetupFileHash *new_hash =
1508 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1510 if (new_hash == NULL)
1511 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1516 void freeSetupFileHash(SetupFileHash *hash)
1521 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1524 char *getHashEntry(SetupFileHash *hash, char *token)
1529 return search_hash_entry(hash, token);
1532 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1539 value_copy = getStringCopy(value);
1541 /* change value; if it does not exist, insert it as new */
1542 if (!change_hash_entry(hash, token, value_copy))
1543 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1544 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1547 char *removeHashEntry(SetupFileHash *hash, char *token)
1552 return remove_hash_entry(hash, token);
1556 static void printSetupFileHash(SetupFileHash *hash)
1558 BEGIN_HASH_ITERATION(hash, itr)
1560 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1561 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1563 END_HASH_ITERATION(hash, itr)
1567 static void *loadSetupFileData(char *filename, boolean use_hash)
1569 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1570 char *token, *value, *line_ptr;
1571 void *setup_file_data, *insert_ptr = NULL;
1572 boolean read_continued_line = FALSE;
1575 if (!(file = fopen(filename, MODE_READ)))
1577 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1583 setup_file_data = newSetupFileHash();
1585 insert_ptr = setup_file_data = newSetupFileList("", "");
1589 /* read next line of input file */
1590 if (!fgets(line, MAX_LINE_LEN, file))
1593 /* cut trailing newline or carriage return */
1594 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1595 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1598 if (read_continued_line)
1600 /* cut leading whitespaces from input line */
1601 for (line_ptr = line; *line_ptr; line_ptr++)
1602 if (*line_ptr != ' ' && *line_ptr != '\t')
1605 /* append new line to existing line, if there is enough space */
1606 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1607 strcat(previous_line, line_ptr);
1609 strcpy(line, previous_line); /* copy storage buffer to line */
1611 read_continued_line = FALSE;
1614 /* if the last character is '\', continue at next line */
1615 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1617 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1618 strcpy(previous_line, line); /* copy line to storage buffer */
1620 read_continued_line = TRUE;
1625 /* cut trailing comment from input line */
1626 for (line_ptr = line; *line_ptr; line_ptr++)
1628 if (*line_ptr == '#')
1635 /* cut trailing whitespaces from input line */
1636 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1637 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1640 /* ignore empty lines */
1644 /* cut leading whitespaces from token */
1645 for (token = line; *token; token++)
1646 if (*token != ' ' && *token != '\t')
1649 /* start with empty value as reliable default */
1652 /* find end of token to determine start of value */
1653 for (line_ptr = token; *line_ptr; line_ptr++)
1655 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1657 *line_ptr = '\0'; /* terminate token string */
1658 value = line_ptr + 1; /* set beginning of value */
1664 /* cut leading whitespaces from value */
1665 for (; *value; value++)
1666 if (*value != ' ' && *value != '\t')
1671 value = "true"; /* treat tokens without value as "true" */
1677 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1679 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1687 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1688 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1692 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1693 SetupFileList *first_valid_list_entry = setup_file_list->next;
1695 /* free empty list header */
1696 setup_file_list->next = NULL;
1697 freeSetupFileList(setup_file_list);
1698 setup_file_data = first_valid_list_entry;
1700 if (first_valid_list_entry == NULL)
1701 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1704 return setup_file_data;
1707 SetupFileList *loadSetupFileList(char *filename)
1709 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1712 SetupFileHash *loadSetupFileHash(char *filename)
1714 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1717 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1718 char *filename, char *identifier)
1720 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1723 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1724 else if (!checkCookieString(value, identifier))
1725 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1729 /* ========================================================================= */
1730 /* setup file stuff */
1731 /* ========================================================================= */
1733 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1734 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1735 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1737 /* level directory info */
1738 #define LEVELINFO_TOKEN_IDENTIFIER 0
1739 #define LEVELINFO_TOKEN_NAME 1
1740 #define LEVELINFO_TOKEN_NAME_SORTING 2
1741 #define LEVELINFO_TOKEN_AUTHOR 3
1742 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1743 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1744 #define LEVELINFO_TOKEN_LEVELS 6
1745 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1746 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1747 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1748 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1749 #define LEVELINFO_TOKEN_READONLY 11
1750 #define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 12
1751 #define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 13
1752 #define LEVELINFO_TOKEN_GRAPHICS_SET 14
1753 #define LEVELINFO_TOKEN_SOUNDS_SET 15
1754 #define LEVELINFO_TOKEN_MUSIC_SET 16
1755 #define LEVELINFO_TOKEN_FILENAME 17
1756 #define LEVELINFO_TOKEN_FILETYPE 18
1757 #define LEVELINFO_TOKEN_HANDICAP 19
1758 #define LEVELINFO_TOKEN_SKIP_LEVELS 20
1760 #define NUM_LEVELINFO_TOKENS 21
1762 static LevelDirTree ldi;
1764 static struct TokenInfo levelinfo_tokens[] =
1766 /* level directory info */
1767 { TYPE_STRING, &ldi.identifier, "identifier" },
1768 { TYPE_STRING, &ldi.name, "name" },
1769 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1770 { TYPE_STRING, &ldi.author, "author" },
1771 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1772 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1773 { TYPE_INTEGER, &ldi.levels, "levels" },
1774 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1775 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1776 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1777 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1778 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1779 { TYPE_STRING, &ldi.graphics_set_ecs, "graphics_set.ecs" },
1780 { TYPE_STRING, &ldi.graphics_set_aga, "graphics_set.aga" },
1781 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1782 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1783 { TYPE_STRING, &ldi.music_set, "music_set" },
1784 { TYPE_STRING, &ldi.level_filename, "filename" },
1785 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1786 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1787 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1790 static void setTreeInfoToDefaults(TreeInfo *ti, int type)
1794 ti->node_top = (ti->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1795 ti->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1796 ti->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1797 ti->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1800 ti->node_parent = NULL;
1801 ti->node_group = NULL;
1808 ti->fullpath = NULL;
1809 ti->basepath = NULL;
1810 ti->identifier = NULL;
1811 ti->name = getStringCopy(ANONYMOUS_NAME);
1812 ti->name_sorting = NULL;
1813 ti->author = getStringCopy(ANONYMOUS_NAME);
1815 ti->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1816 ti->latest_engine = FALSE; /* default: get from level */
1817 ti->parent_link = FALSE;
1818 ti->in_user_dir = FALSE;
1819 ti->user_defined = FALSE;
1821 ti->class_desc = NULL;
1823 ti->infotext = getStringCopy(TREE_INFOTEXT(ti->type));
1825 if (ti->type == TREE_TYPE_LEVEL_DIR)
1827 ti->imported_from = NULL;
1828 ti->imported_by = NULL;
1830 ti->graphics_set_ecs = NULL;
1831 ti->graphics_set_aga = NULL;
1832 ti->graphics_set = NULL;
1833 ti->sounds_set = NULL;
1834 ti->music_set = NULL;
1835 ti->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1836 ti->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1837 ti->music_path = getStringCopy(UNDEFINED_FILENAME);
1839 ti->level_filename = NULL;
1840 ti->level_filetype = NULL;
1843 ti->first_level = 0;
1845 ti->level_group = FALSE;
1846 ti->handicap_level = 0;
1847 ti->readonly = TRUE;
1848 ti->handicap = TRUE;
1849 ti->skip_levels = FALSE;
1853 static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent)
1857 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1859 setTreeInfoToDefaults(ti, TREE_TYPE_UNDEFINED);
1864 /* copy all values from the parent structure */
1866 ti->type = parent->type;
1868 ti->node_top = parent->node_top;
1869 ti->node_parent = parent;
1870 ti->node_group = NULL;
1877 ti->fullpath = NULL;
1878 ti->basepath = NULL;
1879 ti->identifier = NULL;
1880 ti->name = getStringCopy(ANONYMOUS_NAME);
1881 ti->name_sorting = NULL;
1882 ti->author = getStringCopy(parent->author);
1884 ti->sort_priority = parent->sort_priority;
1885 ti->latest_engine = parent->latest_engine;
1886 ti->parent_link = FALSE;
1887 ti->in_user_dir = parent->in_user_dir;
1888 ti->user_defined = parent->user_defined;
1889 ti->color = parent->color;
1890 ti->class_desc = getStringCopy(parent->class_desc);
1892 ti->infotext = getStringCopy(parent->infotext);
1894 if (ti->type == TREE_TYPE_LEVEL_DIR)
1896 ti->imported_from = getStringCopy(parent->imported_from);
1897 ti->imported_by = getStringCopy(parent->imported_by);
1899 ti->graphics_set_ecs = NULL;
1900 ti->graphics_set_aga = NULL;
1901 ti->graphics_set = NULL;
1902 ti->sounds_set = NULL;
1903 ti->music_set = NULL;
1904 ti->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1905 ti->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1906 ti->music_path = getStringCopy(UNDEFINED_FILENAME);
1908 ti->level_filename = NULL;
1909 ti->level_filetype = NULL;
1912 ti->first_level = 0;
1914 ti->level_group = FALSE;
1915 ti->handicap_level = 0;
1916 ti->readonly = TRUE;
1917 ti->handicap = TRUE;
1918 ti->skip_levels = FALSE;
1922 static void freeTreeInfo(TreeInfo *ti)
1924 checked_free(ti->subdir);
1925 checked_free(ti->fullpath);
1926 checked_free(ti->basepath);
1927 checked_free(ti->identifier);
1929 checked_free(ti->name);
1930 checked_free(ti->name_sorting);
1931 checked_free(ti->author);
1933 checked_free(ti->class_desc);
1935 checked_free(ti->infotext);
1937 if (ti->type == TREE_TYPE_LEVEL_DIR)
1939 checked_free(ti->imported_from);
1940 checked_free(ti->imported_by);
1942 checked_free(ti->graphics_set_ecs);
1943 checked_free(ti->graphics_set_aga);
1944 checked_free(ti->graphics_set);
1945 checked_free(ti->sounds_set);
1946 checked_free(ti->music_set);
1948 checked_free(ti->graphics_path);
1949 checked_free(ti->sounds_path);
1950 checked_free(ti->music_path);
1952 checked_free(ti->level_filename);
1953 checked_free(ti->level_filetype);
1957 void setSetupInfo(struct TokenInfo *token_info,
1958 int token_nr, char *token_value)
1960 int token_type = token_info[token_nr].type;
1961 void *setup_value = token_info[token_nr].value;
1963 if (token_value == NULL)
1966 /* set setup field to corresponding token value */
1971 *(boolean *)setup_value = get_boolean_from_string(token_value);
1975 *(Key *)setup_value = getKeyFromKeyName(token_value);
1979 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1983 *(int *)setup_value = get_integer_from_string(token_value);
1987 checked_free(*(char **)setup_value);
1988 *(char **)setup_value = getStringCopy(token_value);
1996 static int compareTreeInfoEntries(const void *object1, const void *object2)
1998 const TreeInfo *entry1 = *((TreeInfo **)object1);
1999 const TreeInfo *entry2 = *((TreeInfo **)object2);
2000 int class_sorting1, class_sorting2;
2003 if (entry1->type == TREE_TYPE_LEVEL_DIR)
2005 class_sorting1 = LEVELSORTING(entry1);
2006 class_sorting2 = LEVELSORTING(entry2);
2010 class_sorting1 = ARTWORKSORTING(entry1);
2011 class_sorting2 = ARTWORKSORTING(entry2);
2014 if (entry1->parent_link || entry2->parent_link)
2015 compare_result = (entry1->parent_link ? -1 : +1);
2016 else if (entry1->sort_priority == entry2->sort_priority)
2018 char *name1 = getStringToLower(entry1->name_sorting);
2019 char *name2 = getStringToLower(entry2->name_sorting);
2021 compare_result = strcmp(name1, name2);
2026 else if (class_sorting1 == class_sorting2)
2027 compare_result = entry1->sort_priority - entry2->sort_priority;
2029 compare_result = class_sorting1 - class_sorting2;
2031 return compare_result;
2034 static void createParentTreeInfoNode(TreeInfo *node_parent)
2038 if (node_parent == NULL)
2041 ti_new = newTreeInfo();
2042 setTreeInfoToDefaults(ti_new, node_parent->type);
2044 ti_new->node_parent = node_parent;
2045 ti_new->parent_link = TRUE;
2047 setString(&ti_new->identifier, node_parent->identifier);
2048 setString(&ti_new->name, ".. (parent directory)");
2049 setString(&ti_new->name_sorting, ti_new->name);
2051 setString(&ti_new->subdir, "..");
2052 setString(&ti_new->fullpath, node_parent->fullpath);
2054 ti_new->sort_priority = node_parent->sort_priority;
2055 ti_new->latest_engine = node_parent->latest_engine;
2057 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
2059 pushTreeInfo(&node_parent->node_group, ti_new);
2062 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
2063 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
2065 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
2066 TreeInfo *node_parent,
2067 char *level_directory,
2068 char *directory_name)
2070 char *directory_path = getPath2(level_directory, directory_name);
2071 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
2072 SetupFileHash *setup_file_hash;
2073 LevelDirTree *leveldir_new = NULL;
2076 /* unless debugging, silently ignore directories without "levelinfo.conf" */
2077 if (!options.debug && !fileExists(filename))
2079 free(directory_path);
2085 setup_file_hash = loadSetupFileHash(filename);
2087 if (setup_file_hash == NULL)
2089 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2091 free(directory_path);
2097 leveldir_new = newTreeInfo();
2100 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
2102 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
2104 leveldir_new->subdir = getStringCopy(directory_name);
2106 checkSetupFileHashIdentifier(setup_file_hash, filename,
2107 getCookie("LEVELINFO"));
2109 /* set all structure fields according to the token/value pairs */
2110 ldi = *leveldir_new;
2111 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2112 setSetupInfo(levelinfo_tokens, i,
2113 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2114 *leveldir_new = ldi;
2116 if (strEqual(leveldir_new->name, ANONYMOUS_NAME))
2117 setString(&leveldir_new->name, leveldir_new->subdir);
2119 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2121 if (leveldir_new->identifier == NULL)
2122 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2124 if (leveldir_new->name_sorting == NULL)
2125 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2127 if (node_parent == NULL) /* top level group */
2129 leveldir_new->basepath = getStringCopy(level_directory);
2130 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2132 else /* sub level group */
2134 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2135 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2139 if (leveldir_new->levels < 1)
2140 leveldir_new->levels = 1;
2143 leveldir_new->last_level =
2144 leveldir_new->first_level + leveldir_new->levels - 1;
2146 leveldir_new->in_user_dir =
2147 (!strEqual(leveldir_new->basepath, options.level_directory));
2149 /* adjust some settings if user's private level directory was detected */
2150 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2151 leveldir_new->in_user_dir &&
2152 (strEqual(leveldir_new->subdir, getLoginName()) ||
2153 strEqual(leveldir_new->name, getLoginName()) ||
2154 strEqual(leveldir_new->author, getRealName())))
2156 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2157 leveldir_new->readonly = FALSE;
2160 leveldir_new->user_defined =
2161 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2163 leveldir_new->color = LEVELCOLOR(leveldir_new);
2165 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2167 leveldir_new->handicap_level = /* set handicap to default value */
2168 (leveldir_new->user_defined || !leveldir_new->handicap ?
2169 leveldir_new->last_level : leveldir_new->first_level);
2172 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2174 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2176 /* skip level sets without levels (which are probably artwork base sets) */
2178 freeSetupFileHash(setup_file_hash);
2179 free(directory_path);
2187 pushTreeInfo(node_first, leveldir_new);
2189 freeSetupFileHash(setup_file_hash);
2191 if (leveldir_new->level_group)
2193 /* create node to link back to current level directory */
2194 createParentTreeInfoNode(leveldir_new);
2196 /* step into sub-directory and look for more level series */
2197 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2198 leveldir_new, directory_path);
2201 free(directory_path);
2207 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2208 TreeInfo *node_parent,
2209 char *level_directory)
2212 struct dirent *dir_entry;
2213 boolean valid_entry_found = FALSE;
2215 if ((dir = opendir(level_directory)) == NULL)
2217 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2221 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2223 struct stat file_status;
2224 char *directory_name = dir_entry->d_name;
2225 char *directory_path = getPath2(level_directory, directory_name);
2227 /* skip entries for current and parent directory */
2228 if (strEqual(directory_name, ".") ||
2229 strEqual(directory_name, ".."))
2231 free(directory_path);
2235 /* find out if directory entry is itself a directory */
2236 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2237 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2239 free(directory_path);
2243 free(directory_path);
2245 if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
2246 strEqual(directory_name, SOUNDS_DIRECTORY) ||
2247 strEqual(directory_name, MUSIC_DIRECTORY))
2250 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2257 /* special case: top level directory may directly contain "levelinfo.conf" */
2258 if (node_parent == NULL && !valid_entry_found)
2260 /* check if this directory directly contains a file "levelinfo.conf" */
2261 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2262 level_directory, ".");
2265 if (!valid_entry_found)
2266 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2270 boolean AdjustGraphicsForEMC()
2272 boolean settings_changed = FALSE;
2274 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all);
2275 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first);
2277 return settings_changed;
2280 void LoadLevelInfo()
2282 InitUserLevelDirectory(getLoginName());
2284 DrawInitText("Loading level series:", 120, FC_GREEN);
2286 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2287 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2289 /* after loading all level set information, clone the level directory tree
2290 and remove all level sets without levels (these may still contain artwork
2291 to be offered in the setup menu as "custom artwork", and are therefore
2292 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2293 leveldir_first_all = leveldir_first;
2294 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2296 AdjustGraphicsForEMC();
2298 /* before sorting, the first entries will be from the user directory */
2299 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2301 if (leveldir_first == NULL)
2302 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2304 sortTreeInfo(&leveldir_first);
2307 dumpTreeInfo(leveldir_first, 0);
2311 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2312 TreeInfo *node_parent,
2313 char *base_directory,
2314 char *directory_name, int type)
2316 char *directory_path = getPath2(base_directory, directory_name);
2317 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2318 SetupFileHash *setup_file_hash = NULL;
2319 TreeInfo *artwork_new = NULL;
2322 if (fileExists(filename))
2323 setup_file_hash = loadSetupFileHash(filename);
2325 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2328 struct dirent *dir_entry;
2329 boolean valid_file_found = FALSE;
2331 if ((dir = opendir(directory_path)) != NULL)
2333 while ((dir_entry = readdir(dir)) != NULL)
2335 char *entry_name = dir_entry->d_name;
2337 if (FileIsArtworkType(entry_name, type))
2339 valid_file_found = TRUE;
2347 if (!valid_file_found)
2349 if (!strEqual(directory_name, "."))
2350 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2352 free(directory_path);
2359 artwork_new = newTreeInfo();
2362 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2364 setTreeInfoToDefaults(artwork_new, type);
2366 artwork_new->subdir = getStringCopy(directory_name);
2368 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2371 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2374 /* set all structure fields according to the token/value pairs */
2376 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2377 setSetupInfo(levelinfo_tokens, i,
2378 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2381 if (strEqual(artwork_new->name, ANONYMOUS_NAME))
2382 setString(&artwork_new->name, artwork_new->subdir);
2385 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2388 if (artwork_new->identifier == NULL)
2389 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2391 if (artwork_new->name_sorting == NULL)
2392 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2395 if (node_parent == NULL) /* top level group */
2397 artwork_new->basepath = getStringCopy(base_directory);
2398 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2400 else /* sub level group */
2402 artwork_new->basepath = getStringCopy(node_parent->basepath);
2403 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2406 artwork_new->in_user_dir =
2407 (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)));
2409 /* (may use ".sort_priority" from "setup_file_hash" above) */
2410 artwork_new->color = ARTWORKCOLOR(artwork_new);
2412 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2414 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2416 if (strEqual(artwork_new->subdir, "."))
2418 if (artwork_new->user_defined)
2420 setString(&artwork_new->identifier, "private");
2421 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2425 setString(&artwork_new->identifier, "classic");
2426 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2429 /* set to new values after changing ".sort_priority" */
2430 artwork_new->color = ARTWORKCOLOR(artwork_new);
2432 setString(&artwork_new->class_desc,
2433 getLevelClassDescription(artwork_new));
2437 setString(&artwork_new->identifier, artwork_new->subdir);
2440 setString(&artwork_new->name, artwork_new->identifier);
2441 setString(&artwork_new->name_sorting, artwork_new->name);
2444 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2446 pushTreeInfo(node_first, artwork_new);
2448 freeSetupFileHash(setup_file_hash);
2450 free(directory_path);
2456 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2457 TreeInfo *node_parent,
2458 char *base_directory, int type)
2461 struct dirent *dir_entry;
2462 boolean valid_entry_found = FALSE;
2464 if ((dir = opendir(base_directory)) == NULL)
2466 /* display error if directory is main "options.graphics_directory" etc. */
2467 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2468 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2473 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2475 struct stat file_status;
2476 char *directory_name = dir_entry->d_name;
2477 char *directory_path = getPath2(base_directory, directory_name);
2479 /* skip directory entries for current and parent directory */
2480 if (strEqual(directory_name, ".") ||
2481 strEqual(directory_name, ".."))
2483 free(directory_path);
2487 /* skip directory entries which are not a directory or are not accessible */
2488 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2489 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2491 free(directory_path);
2495 free(directory_path);
2497 /* check if this directory contains artwork with or without config file */
2498 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2500 directory_name, type);
2505 /* check if this directory directly contains artwork itself */
2506 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2507 base_directory, ".",
2509 if (!valid_entry_found)
2510 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2514 static TreeInfo *getDummyArtworkInfo(int type)
2516 /* this is only needed when there is completely no artwork available */
2517 TreeInfo *artwork_new = newTreeInfo();
2519 setTreeInfoToDefaults(artwork_new, type);
2521 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2522 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2523 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2525 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2526 setString(&artwork_new->name, UNDEFINED_FILENAME);
2527 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2532 void LoadArtworkInfo()
2534 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2536 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2537 options.graphics_directory,
2538 TREE_TYPE_GRAPHICS_DIR);
2539 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2540 getUserGraphicsDir(),
2541 TREE_TYPE_GRAPHICS_DIR);
2543 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2544 options.sounds_directory,
2545 TREE_TYPE_SOUNDS_DIR);
2546 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2548 TREE_TYPE_SOUNDS_DIR);
2550 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2551 options.music_directory,
2552 TREE_TYPE_MUSIC_DIR);
2553 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2555 TREE_TYPE_MUSIC_DIR);
2557 if (artwork.gfx_first == NULL)
2558 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2559 if (artwork.snd_first == NULL)
2560 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2561 if (artwork.mus_first == NULL)
2562 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2564 /* before sorting, the first entries will be from the user directory */
2565 artwork.gfx_current =
2566 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2567 if (artwork.gfx_current == NULL)
2568 artwork.gfx_current =
2569 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2570 if (artwork.gfx_current == NULL)
2571 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
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);
2581 artwork.mus_current =
2582 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2583 if (artwork.mus_current == NULL)
2584 artwork.mus_current =
2585 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2586 if (artwork.mus_current == NULL)
2587 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2589 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2590 artwork.snd_current_identifier = artwork.snd_current->identifier;
2591 artwork.mus_current_identifier = artwork.mus_current->identifier;
2594 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2595 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2596 printf("music set == %s\n\n", artwork.mus_current_identifier);
2599 sortTreeInfo(&artwork.gfx_first);
2600 sortTreeInfo(&artwork.snd_first);
2601 sortTreeInfo(&artwork.mus_first);
2604 dumpTreeInfo(artwork.gfx_first, 0);
2605 dumpTreeInfo(artwork.snd_first, 0);
2606 dumpTreeInfo(artwork.mus_first, 0);
2610 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2611 LevelDirTree *level_node)
2613 /* recursively check all level directories for artwork sub-directories */
2617 /* check all tree entries for artwork, but skip parent link entries */
2618 if (!level_node->parent_link)
2620 TreeInfo *topnode_last = *artwork_node;
2621 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2622 ARTWORK_DIRECTORY((*artwork_node)->type));
2624 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2625 (*artwork_node)->type);
2627 if (topnode_last != *artwork_node)
2629 free((*artwork_node)->identifier);
2630 free((*artwork_node)->name);
2631 free((*artwork_node)->name_sorting);
2633 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2634 (*artwork_node)->name = getStringCopy(level_node->name);
2635 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2637 (*artwork_node)->sort_priority = level_node->sort_priority;
2638 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2644 if (level_node->node_group != NULL)
2645 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2647 level_node = level_node->next;
2651 void LoadLevelArtworkInfo()
2653 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2655 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2656 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2657 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2659 /* needed for reloading level artwork not known at ealier stage */
2661 if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set))
2663 artwork.gfx_current =
2664 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2665 if (artwork.gfx_current == NULL)
2666 artwork.gfx_current =
2667 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2668 if (artwork.gfx_current == NULL)
2669 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2672 if (!strEqual(artwork.snd_current_identifier, setup.sounds_set))
2674 artwork.snd_current =
2675 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2676 if (artwork.snd_current == NULL)
2677 artwork.snd_current =
2678 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2679 if (artwork.snd_current == NULL)
2680 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2683 if (!strEqual(artwork.mus_current_identifier, setup.music_set))
2685 artwork.mus_current =
2686 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2687 if (artwork.mus_current == NULL)
2688 artwork.mus_current =
2689 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2690 if (artwork.mus_current == NULL)
2691 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2694 sortTreeInfo(&artwork.gfx_first);
2695 sortTreeInfo(&artwork.snd_first);
2696 sortTreeInfo(&artwork.mus_first);
2699 dumpTreeInfo(artwork.gfx_first, 0);
2700 dumpTreeInfo(artwork.snd_first, 0);
2701 dumpTreeInfo(artwork.mus_first, 0);
2705 static void SaveUserLevelInfo()
2707 LevelDirTree *level_info;
2712 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2714 if (!(file = fopen(filename, MODE_WRITE)))
2716 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2721 level_info = newTreeInfo();
2723 /* always start with reliable default values */
2724 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2726 setString(&level_info->name, getLoginName());
2727 setString(&level_info->author, getRealName());
2728 level_info->levels = 100;
2729 level_info->first_level = 1;
2731 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2733 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2734 getCookie("LEVELINFO")));
2737 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2739 if (i == LEVELINFO_TOKEN_NAME ||
2740 i == LEVELINFO_TOKEN_AUTHOR ||
2741 i == LEVELINFO_TOKEN_LEVELS ||
2742 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2743 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2745 /* just to make things nicer :) */
2746 if (i == LEVELINFO_TOKEN_AUTHOR)
2747 fprintf(file, "\n");
2750 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2754 SetFilePermissions(filename, PERMS_PRIVATE);
2756 freeTreeInfo(level_info);
2760 char *getSetupValue(int type, void *value)
2762 static char value_string[MAX_LINE_LEN];
2770 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2774 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2778 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2782 strcpy(value_string, (*(boolean *)value ? "AGA" : "ECS"));
2786 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2790 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2794 sprintf(value_string, "%d", *(int *)value);
2798 strcpy(value_string, *(char **)value);
2802 value_string[0] = '\0';
2806 if (type & TYPE_GHOSTED)
2807 strcpy(value_string, "n/a");
2809 return value_string;
2812 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2816 static char token_string[MAX_LINE_LEN];
2817 int token_type = token_info[token_nr].type;
2818 void *setup_value = token_info[token_nr].value;
2819 char *token_text = token_info[token_nr].text;
2820 char *value_string = getSetupValue(token_type, setup_value);
2822 /* build complete token string */
2823 sprintf(token_string, "%s%s", prefix, token_text);
2825 /* build setup entry line */
2826 line = getFormattedSetupEntry(token_string, value_string);
2828 if (token_type == TYPE_KEY_X11)
2830 Key key = *(Key *)setup_value;
2831 char *keyname = getKeyNameFromKey(key);
2833 /* add comment, if useful */
2834 if (!strEqual(keyname, "(undefined)") &&
2835 !strEqual(keyname, "(unknown)"))
2837 /* add at least one whitespace */
2839 for (i = strlen(line); i < token_comment_position; i++)
2843 strcat(line, keyname);
2850 void LoadLevelSetup_LastSeries()
2852 /* ----------------------------------------------------------------------- */
2853 /* ~/.<program>/levelsetup.conf */
2854 /* ----------------------------------------------------------------------- */
2856 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2857 SetupFileHash *level_setup_hash = NULL;
2859 /* always start with reliable default values */
2860 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2862 if ((level_setup_hash = loadSetupFileHash(filename)))
2864 char *last_level_series =
2865 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2867 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2869 if (leveldir_current == NULL)
2870 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2872 checkSetupFileHashIdentifier(level_setup_hash, filename,
2873 getCookie("LEVELSETUP"));
2875 freeSetupFileHash(level_setup_hash);
2878 Error(ERR_WARN, "using default setup values");
2883 void SaveLevelSetup_LastSeries()
2885 /* ----------------------------------------------------------------------- */
2886 /* ~/.<program>/levelsetup.conf */
2887 /* ----------------------------------------------------------------------- */
2889 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2890 char *level_subdir = leveldir_current->subdir;
2893 InitUserDataDirectory();
2895 if (!(file = fopen(filename, MODE_WRITE)))
2897 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2902 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2903 getCookie("LEVELSETUP")));
2904 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2909 SetFilePermissions(filename, PERMS_PRIVATE);
2914 static void checkSeriesInfo()
2916 static char *level_directory = NULL;
2918 struct dirent *dir_entry;
2920 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2922 level_directory = getPath2((leveldir_current->in_user_dir ?
2923 getUserLevelDir(NULL) :
2924 options.level_directory),
2925 leveldir_current->fullpath);
2927 if ((dir = opendir(level_directory)) == NULL)
2929 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2933 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2935 if (strlen(dir_entry->d_name) > 4 &&
2936 dir_entry->d_name[3] == '.' &&
2937 strEqual(&dir_entry->d_name[4], LEVELFILE_EXTENSION))
2939 char levelnum_str[4];
2942 strncpy(levelnum_str, dir_entry->d_name, 3);
2943 levelnum_str[3] = '\0';
2945 levelnum_value = atoi(levelnum_str);
2948 if (levelnum_value < leveldir_current->first_level)
2950 Error(ERR_WARN, "additional level %d found", levelnum_value);
2951 leveldir_current->first_level = levelnum_value;
2953 else if (levelnum_value > leveldir_current->last_level)
2955 Error(ERR_WARN, "additional level %d found", levelnum_value);
2956 leveldir_current->last_level = levelnum_value;
2965 void LoadLevelSetup_SeriesInfo()
2968 SetupFileHash *level_setup_hash = NULL;
2969 char *level_subdir = leveldir_current->subdir;
2971 /* always start with reliable default values */
2972 level_nr = leveldir_current->first_level;
2974 checkSeriesInfo(leveldir_current);
2976 /* ----------------------------------------------------------------------- */
2977 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2978 /* ----------------------------------------------------------------------- */
2980 level_subdir = leveldir_current->subdir;
2982 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2984 if ((level_setup_hash = loadSetupFileHash(filename)))
2988 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2992 level_nr = atoi(token_value);
2994 if (level_nr < leveldir_current->first_level)
2995 level_nr = leveldir_current->first_level;
2996 if (level_nr > leveldir_current->last_level)
2997 level_nr = leveldir_current->last_level;
3000 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
3004 int level_nr = atoi(token_value);
3006 if (level_nr < leveldir_current->first_level)
3007 level_nr = leveldir_current->first_level;
3008 if (level_nr > leveldir_current->last_level + 1)
3009 level_nr = leveldir_current->last_level;
3011 if (leveldir_current->user_defined || !leveldir_current->handicap)
3012 level_nr = leveldir_current->last_level;
3014 leveldir_current->handicap_level = level_nr;
3017 checkSetupFileHashIdentifier(level_setup_hash, filename,
3018 getCookie("LEVELSETUP"));
3020 freeSetupFileHash(level_setup_hash);
3023 Error(ERR_WARN, "using default setup values");
3028 void SaveLevelSetup_SeriesInfo()
3031 char *level_subdir = leveldir_current->subdir;
3032 char *level_nr_str = int2str(level_nr, 0);
3033 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3036 /* ----------------------------------------------------------------------- */
3037 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3038 /* ----------------------------------------------------------------------- */
3040 InitLevelSetupDirectory(level_subdir);
3042 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3044 if (!(file = fopen(filename, MODE_WRITE)))
3046 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3051 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3052 getCookie("LEVELSETUP")));
3053 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3055 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3056 handicap_level_str));
3060 SetFilePermissions(filename, PERMS_PRIVATE);