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>
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_directory);
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 if (program.userdata_path == NULL)
1209 program.userdata_path = getPath2(getPersonalDataDir(),
1210 program.userdata_subdir);
1212 return program.userdata_path;
1215 void updateUserGameDataDir()
1217 #if defined(PLATFORM_MACOSX)
1218 char *userdata_dir_old = getPath2(getHomeDir(), program.userdata_subdir_unix);
1219 char *userdata_dir_new = getUserGameDataDir();
1221 /* convert old Unix style game data directory to Mac OS X style, if needed */
1222 if (fileExists(userdata_dir_old) && !fileExists(userdata_dir_new))
1224 if (rename(userdata_dir_old, userdata_dir_new) != 0)
1226 Error(ERR_WARN, "cannot move game data directory '%s' to '%s'",
1227 userdata_dir_old, userdata_dir_new);
1229 /* continue using Unix style data directory -- this should not happen */
1230 program.userdata_path = getPath2(getPersonalDataDir(),
1231 program.userdata_subdir_unix);
1235 free(userdata_dir_old);
1236 free(userdata_dir_new);
1242 return getUserGameDataDir();
1245 static mode_t posix_umask(mode_t mask)
1247 #if defined(PLATFORM_UNIX)
1254 static int posix_mkdir(const char *pathname, mode_t mode)
1256 #if defined(PLATFORM_WIN32)
1257 return mkdir(pathname);
1259 return mkdir(pathname, mode);
1263 void createDirectory(char *dir, char *text, int permission_class)
1265 /* leave "other" permissions in umask untouched, but ensure group parts
1266 of USERDATA_DIR_MODE are not masked */
1267 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1268 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1269 mode_t normal_umask = posix_umask(0);
1270 mode_t group_umask = ~(dir_mode & S_IRWXG);
1271 posix_umask(normal_umask & group_umask);
1273 if (!fileExists(dir))
1274 if (posix_mkdir(dir, dir_mode) != 0)
1275 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1277 posix_umask(normal_umask); /* reset normal umask */
1280 void InitUserDataDirectory()
1282 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
1285 void SetFilePermissions(char *filename, int permission_class)
1287 chmod(filename, (permission_class == PERMS_PRIVATE ?
1288 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1291 char *getCookie(char *file_type)
1293 static char cookie[MAX_COOKIE_LEN + 1];
1295 if (strlen(program.cookie_prefix) + 1 +
1296 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1297 return "[COOKIE ERROR]"; /* should never happen */
1299 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1300 program.cookie_prefix, file_type,
1301 program.version_major, program.version_minor);
1306 int getFileVersionFromCookieString(const char *cookie)
1308 const char *ptr_cookie1, *ptr_cookie2;
1309 const char *pattern1 = "_FILE_VERSION_";
1310 const char *pattern2 = "?.?";
1311 const int len_cookie = strlen(cookie);
1312 const int len_pattern1 = strlen(pattern1);
1313 const int len_pattern2 = strlen(pattern2);
1314 const int len_pattern = len_pattern1 + len_pattern2;
1315 int version_major, version_minor;
1317 if (len_cookie <= len_pattern)
1320 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1321 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1323 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1326 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1327 ptr_cookie2[1] != '.' ||
1328 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1331 version_major = ptr_cookie2[0] - '0';
1332 version_minor = ptr_cookie2[2] - '0';
1334 return VERSION_IDENT(version_major, version_minor, 0, 0);
1337 boolean checkCookieString(const char *cookie, const char *template)
1339 const char *pattern = "_FILE_VERSION_?.?";
1340 const int len_cookie = strlen(cookie);
1341 const int len_template = strlen(template);
1342 const int len_pattern = strlen(pattern);
1344 if (len_cookie != len_template)
1347 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1353 /* ------------------------------------------------------------------------- */
1354 /* setup file list and hash handling functions */
1355 /* ------------------------------------------------------------------------- */
1357 char *getFormattedSetupEntry(char *token, char *value)
1360 static char entry[MAX_LINE_LEN];
1362 /* if value is an empty string, just return token without value */
1366 /* start with the token and some spaces to format output line */
1367 sprintf(entry, "%s:", token);
1368 for (i = strlen(entry); i < token_value_position; i++)
1371 /* continue with the token's value */
1372 strcat(entry, value);
1377 SetupFileList *newSetupFileList(char *token, char *value)
1379 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1381 new->token = getStringCopy(token);
1382 new->value = getStringCopy(value);
1389 void freeSetupFileList(SetupFileList *list)
1394 checked_free(list->token);
1395 checked_free(list->value);
1398 freeSetupFileList(list->next);
1403 char *getListEntry(SetupFileList *list, char *token)
1408 if (strEqual(list->token, token))
1411 return getListEntry(list->next, token);
1414 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1419 if (strEqual(list->token, token))
1421 checked_free(list->value);
1423 list->value = getStringCopy(value);
1427 else if (list->next == NULL)
1428 return (list->next = newSetupFileList(token, value));
1430 return setListEntry(list->next, token, value);
1433 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1438 if (list->next == NULL)
1439 return (list->next = newSetupFileList(token, value));
1441 return addListEntry(list->next, token, value);
1445 static void printSetupFileList(SetupFileList *list)
1450 printf("token: '%s'\n", list->token);
1451 printf("value: '%s'\n", list->value);
1453 printSetupFileList(list->next);
1458 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1459 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1460 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1461 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1463 #define insert_hash_entry hashtable_insert
1464 #define search_hash_entry hashtable_search
1465 #define change_hash_entry hashtable_change
1466 #define remove_hash_entry hashtable_remove
1469 static unsigned int get_hash_from_key(void *key)
1474 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1475 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1476 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1477 it works better than many other constants, prime or not) has never been
1478 adequately explained.
1480 If you just want to have a good hash function, and cannot wait, djb2
1481 is one of the best string hash functions i know. It has excellent
1482 distribution and speed on many different sets of keys and table sizes.
1483 You are not likely to do better with one of the "well known" functions
1484 such as PJW, K&R, etc.
1486 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1489 char *str = (char *)key;
1490 unsigned int hash = 5381;
1493 while ((c = *str++))
1494 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1499 static int keys_are_equal(void *key1, void *key2)
1501 return (strEqual((char *)key1, (char *)key2));
1504 SetupFileHash *newSetupFileHash()
1506 SetupFileHash *new_hash =
1507 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1509 if (new_hash == NULL)
1510 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1515 void freeSetupFileHash(SetupFileHash *hash)
1520 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1523 char *getHashEntry(SetupFileHash *hash, char *token)
1528 return search_hash_entry(hash, token);
1531 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1538 value_copy = getStringCopy(value);
1540 /* change value; if it does not exist, insert it as new */
1541 if (!change_hash_entry(hash, token, value_copy))
1542 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1543 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1546 char *removeHashEntry(SetupFileHash *hash, char *token)
1551 return remove_hash_entry(hash, token);
1555 static void printSetupFileHash(SetupFileHash *hash)
1557 BEGIN_HASH_ITERATION(hash, itr)
1559 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1560 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1562 END_HASH_ITERATION(hash, itr)
1566 static void *loadSetupFileData(char *filename, boolean use_hash)
1568 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1569 char *token, *value, *line_ptr;
1570 void *setup_file_data, *insert_ptr = NULL;
1571 boolean read_continued_line = FALSE;
1574 if (!(file = fopen(filename, MODE_READ)))
1576 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1582 setup_file_data = newSetupFileHash();
1584 insert_ptr = setup_file_data = newSetupFileList("", "");
1588 /* read next line of input file */
1589 if (!fgets(line, MAX_LINE_LEN, file))
1592 /* cut trailing newline or carriage return */
1593 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1594 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1597 if (read_continued_line)
1599 /* cut leading whitespaces from input line */
1600 for (line_ptr = line; *line_ptr; line_ptr++)
1601 if (*line_ptr != ' ' && *line_ptr != '\t')
1604 /* append new line to existing line, if there is enough space */
1605 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1606 strcat(previous_line, line_ptr);
1608 strcpy(line, previous_line); /* copy storage buffer to line */
1610 read_continued_line = FALSE;
1613 /* if the last character is '\', continue at next line */
1614 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1616 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1617 strcpy(previous_line, line); /* copy line to storage buffer */
1619 read_continued_line = TRUE;
1624 /* cut trailing comment from input line */
1625 for (line_ptr = line; *line_ptr; line_ptr++)
1627 if (*line_ptr == '#')
1634 /* cut trailing whitespaces from input line */
1635 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1636 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1639 /* ignore empty lines */
1643 /* cut leading whitespaces from token */
1644 for (token = line; *token; token++)
1645 if (*token != ' ' && *token != '\t')
1648 /* start with empty value as reliable default */
1651 /* find end of token to determine start of value */
1652 for (line_ptr = token; *line_ptr; line_ptr++)
1654 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1656 *line_ptr = '\0'; /* terminate token string */
1657 value = line_ptr + 1; /* set beginning of value */
1663 /* cut leading whitespaces from value */
1664 for (; *value; value++)
1665 if (*value != ' ' && *value != '\t')
1670 value = "true"; /* treat tokens without value as "true" */
1676 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1678 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1686 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1687 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1691 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1692 SetupFileList *first_valid_list_entry = setup_file_list->next;
1694 /* free empty list header */
1695 setup_file_list->next = NULL;
1696 freeSetupFileList(setup_file_list);
1697 setup_file_data = first_valid_list_entry;
1699 if (first_valid_list_entry == NULL)
1700 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1703 return setup_file_data;
1706 SetupFileList *loadSetupFileList(char *filename)
1708 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1711 SetupFileHash *loadSetupFileHash(char *filename)
1713 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1716 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1717 char *filename, char *identifier)
1719 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1722 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1723 else if (!checkCookieString(value, identifier))
1724 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1728 /* ========================================================================= */
1729 /* setup file stuff */
1730 /* ========================================================================= */
1732 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1733 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1734 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1736 /* level directory info */
1737 #define LEVELINFO_TOKEN_IDENTIFIER 0
1738 #define LEVELINFO_TOKEN_NAME 1
1739 #define LEVELINFO_TOKEN_NAME_SORTING 2
1740 #define LEVELINFO_TOKEN_AUTHOR 3
1741 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1742 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1743 #define LEVELINFO_TOKEN_LEVELS 6
1744 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1745 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1746 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1747 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1748 #define LEVELINFO_TOKEN_READONLY 11
1749 #define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 12
1750 #define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 13
1751 #define LEVELINFO_TOKEN_GRAPHICS_SET 14
1752 #define LEVELINFO_TOKEN_SOUNDS_SET 15
1753 #define LEVELINFO_TOKEN_MUSIC_SET 16
1754 #define LEVELINFO_TOKEN_FILENAME 17
1755 #define LEVELINFO_TOKEN_FILETYPE 18
1756 #define LEVELINFO_TOKEN_HANDICAP 19
1757 #define LEVELINFO_TOKEN_SKIP_LEVELS 20
1759 #define NUM_LEVELINFO_TOKENS 21
1761 static LevelDirTree ldi;
1763 static struct TokenInfo levelinfo_tokens[] =
1765 /* level directory info */
1766 { TYPE_STRING, &ldi.identifier, "identifier" },
1767 { TYPE_STRING, &ldi.name, "name" },
1768 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1769 { TYPE_STRING, &ldi.author, "author" },
1770 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1771 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1772 { TYPE_INTEGER, &ldi.levels, "levels" },
1773 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1774 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1775 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1776 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1777 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1778 { TYPE_STRING, &ldi.graphics_set_ecs, "graphics_set.ecs" },
1779 { TYPE_STRING, &ldi.graphics_set_aga, "graphics_set.aga" },
1780 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1781 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1782 { TYPE_STRING, &ldi.music_set, "music_set" },
1783 { TYPE_STRING, &ldi.level_filename, "filename" },
1784 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1785 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1786 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1789 static void setTreeInfoToDefaults(TreeInfo *ti, int type)
1793 ti->node_top = (ti->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1794 ti->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1795 ti->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1796 ti->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1799 ti->node_parent = NULL;
1800 ti->node_group = NULL;
1807 ti->fullpath = NULL;
1808 ti->basepath = NULL;
1809 ti->identifier = NULL;
1810 ti->name = getStringCopy(ANONYMOUS_NAME);
1811 ti->name_sorting = NULL;
1812 ti->author = getStringCopy(ANONYMOUS_NAME);
1814 ti->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1815 ti->latest_engine = FALSE; /* default: get from level */
1816 ti->parent_link = FALSE;
1817 ti->in_user_dir = FALSE;
1818 ti->user_defined = FALSE;
1820 ti->class_desc = NULL;
1822 ti->infotext = getStringCopy(TREE_INFOTEXT(ti->type));
1824 if (ti->type == TREE_TYPE_LEVEL_DIR)
1826 ti->imported_from = NULL;
1827 ti->imported_by = NULL;
1829 ti->graphics_set_ecs = NULL;
1830 ti->graphics_set_aga = NULL;
1831 ti->graphics_set = NULL;
1832 ti->sounds_set = NULL;
1833 ti->music_set = NULL;
1834 ti->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1835 ti->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1836 ti->music_path = getStringCopy(UNDEFINED_FILENAME);
1838 ti->level_filename = NULL;
1839 ti->level_filetype = NULL;
1842 ti->first_level = 0;
1844 ti->level_group = FALSE;
1845 ti->handicap_level = 0;
1846 ti->readonly = TRUE;
1847 ti->handicap = TRUE;
1848 ti->skip_levels = FALSE;
1852 static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent)
1856 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1858 setTreeInfoToDefaults(ti, TREE_TYPE_UNDEFINED);
1863 /* copy all values from the parent structure */
1865 ti->type = parent->type;
1867 ti->node_top = parent->node_top;
1868 ti->node_parent = parent;
1869 ti->node_group = NULL;
1876 ti->fullpath = NULL;
1877 ti->basepath = NULL;
1878 ti->identifier = NULL;
1879 ti->name = getStringCopy(ANONYMOUS_NAME);
1880 ti->name_sorting = NULL;
1881 ti->author = getStringCopy(parent->author);
1883 ti->sort_priority = parent->sort_priority;
1884 ti->latest_engine = parent->latest_engine;
1885 ti->parent_link = FALSE;
1886 ti->in_user_dir = parent->in_user_dir;
1887 ti->user_defined = parent->user_defined;
1888 ti->color = parent->color;
1889 ti->class_desc = getStringCopy(parent->class_desc);
1891 ti->infotext = getStringCopy(parent->infotext);
1893 if (ti->type == TREE_TYPE_LEVEL_DIR)
1895 ti->imported_from = getStringCopy(parent->imported_from);
1896 ti->imported_by = getStringCopy(parent->imported_by);
1898 ti->graphics_set_ecs = NULL;
1899 ti->graphics_set_aga = NULL;
1900 ti->graphics_set = NULL;
1901 ti->sounds_set = NULL;
1902 ti->music_set = NULL;
1903 ti->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1904 ti->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1905 ti->music_path = getStringCopy(UNDEFINED_FILENAME);
1907 ti->level_filename = NULL;
1908 ti->level_filetype = NULL;
1911 ti->first_level = 0;
1913 ti->level_group = FALSE;
1914 ti->handicap_level = 0;
1915 ti->readonly = TRUE;
1916 ti->handicap = TRUE;
1917 ti->skip_levels = FALSE;
1921 static void freeTreeInfo(TreeInfo *ti)
1923 checked_free(ti->subdir);
1924 checked_free(ti->fullpath);
1925 checked_free(ti->basepath);
1926 checked_free(ti->identifier);
1928 checked_free(ti->name);
1929 checked_free(ti->name_sorting);
1930 checked_free(ti->author);
1932 checked_free(ti->class_desc);
1934 checked_free(ti->infotext);
1936 if (ti->type == TREE_TYPE_LEVEL_DIR)
1938 checked_free(ti->imported_from);
1939 checked_free(ti->imported_by);
1941 checked_free(ti->graphics_set_ecs);
1942 checked_free(ti->graphics_set_aga);
1943 checked_free(ti->graphics_set);
1944 checked_free(ti->sounds_set);
1945 checked_free(ti->music_set);
1947 checked_free(ti->graphics_path);
1948 checked_free(ti->sounds_path);
1949 checked_free(ti->music_path);
1951 checked_free(ti->level_filename);
1952 checked_free(ti->level_filetype);
1956 void setSetupInfo(struct TokenInfo *token_info,
1957 int token_nr, char *token_value)
1959 int token_type = token_info[token_nr].type;
1960 void *setup_value = token_info[token_nr].value;
1962 if (token_value == NULL)
1965 /* set setup field to corresponding token value */
1970 *(boolean *)setup_value = get_boolean_from_string(token_value);
1974 *(Key *)setup_value = getKeyFromKeyName(token_value);
1978 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1982 *(int *)setup_value = get_integer_from_string(token_value);
1986 checked_free(*(char **)setup_value);
1987 *(char **)setup_value = getStringCopy(token_value);
1995 static int compareTreeInfoEntries(const void *object1, const void *object2)
1997 const TreeInfo *entry1 = *((TreeInfo **)object1);
1998 const TreeInfo *entry2 = *((TreeInfo **)object2);
1999 int class_sorting1, class_sorting2;
2002 if (entry1->type == TREE_TYPE_LEVEL_DIR)
2004 class_sorting1 = LEVELSORTING(entry1);
2005 class_sorting2 = LEVELSORTING(entry2);
2009 class_sorting1 = ARTWORKSORTING(entry1);
2010 class_sorting2 = ARTWORKSORTING(entry2);
2013 if (entry1->parent_link || entry2->parent_link)
2014 compare_result = (entry1->parent_link ? -1 : +1);
2015 else if (entry1->sort_priority == entry2->sort_priority)
2017 char *name1 = getStringToLower(entry1->name_sorting);
2018 char *name2 = getStringToLower(entry2->name_sorting);
2020 compare_result = strcmp(name1, name2);
2025 else if (class_sorting1 == class_sorting2)
2026 compare_result = entry1->sort_priority - entry2->sort_priority;
2028 compare_result = class_sorting1 - class_sorting2;
2030 return compare_result;
2033 static void createParentTreeInfoNode(TreeInfo *node_parent)
2037 if (node_parent == NULL)
2040 ti_new = newTreeInfo();
2041 setTreeInfoToDefaults(ti_new, node_parent->type);
2043 ti_new->node_parent = node_parent;
2044 ti_new->parent_link = TRUE;
2046 setString(&ti_new->identifier, node_parent->identifier);
2047 setString(&ti_new->name, ".. (parent directory)");
2048 setString(&ti_new->name_sorting, ti_new->name);
2050 setString(&ti_new->subdir, "..");
2051 setString(&ti_new->fullpath, node_parent->fullpath);
2053 ti_new->sort_priority = node_parent->sort_priority;
2054 ti_new->latest_engine = node_parent->latest_engine;
2056 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
2058 pushTreeInfo(&node_parent->node_group, ti_new);
2061 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
2062 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
2064 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
2065 TreeInfo *node_parent,
2066 char *level_directory,
2067 char *directory_name)
2069 char *directory_path = getPath2(level_directory, directory_name);
2070 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
2071 SetupFileHash *setup_file_hash;
2072 LevelDirTree *leveldir_new = NULL;
2075 /* unless debugging, silently ignore directories without "levelinfo.conf" */
2076 if (!options.debug && !fileExists(filename))
2078 free(directory_path);
2084 setup_file_hash = loadSetupFileHash(filename);
2086 if (setup_file_hash == NULL)
2088 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2090 free(directory_path);
2096 leveldir_new = newTreeInfo();
2099 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
2101 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
2103 leveldir_new->subdir = getStringCopy(directory_name);
2105 checkSetupFileHashIdentifier(setup_file_hash, filename,
2106 getCookie("LEVELINFO"));
2108 /* set all structure fields according to the token/value pairs */
2109 ldi = *leveldir_new;
2110 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2111 setSetupInfo(levelinfo_tokens, i,
2112 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2113 *leveldir_new = ldi;
2115 if (strEqual(leveldir_new->name, ANONYMOUS_NAME))
2116 setString(&leveldir_new->name, leveldir_new->subdir);
2118 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2120 if (leveldir_new->identifier == NULL)
2121 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2123 if (leveldir_new->name_sorting == NULL)
2124 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2126 if (node_parent == NULL) /* top level group */
2128 leveldir_new->basepath = getStringCopy(level_directory);
2129 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2131 else /* sub level group */
2133 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2134 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2138 if (leveldir_new->levels < 1)
2139 leveldir_new->levels = 1;
2142 leveldir_new->last_level =
2143 leveldir_new->first_level + leveldir_new->levels - 1;
2145 leveldir_new->in_user_dir =
2146 (!strEqual(leveldir_new->basepath, options.level_directory));
2148 /* adjust some settings if user's private level directory was detected */
2149 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2150 leveldir_new->in_user_dir &&
2151 (strEqual(leveldir_new->subdir, getLoginName()) ||
2152 strEqual(leveldir_new->name, getLoginName()) ||
2153 strEqual(leveldir_new->author, getRealName())))
2155 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2156 leveldir_new->readonly = FALSE;
2159 leveldir_new->user_defined =
2160 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2162 leveldir_new->color = LEVELCOLOR(leveldir_new);
2164 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2166 leveldir_new->handicap_level = /* set handicap to default value */
2167 (leveldir_new->user_defined || !leveldir_new->handicap ?
2168 leveldir_new->last_level : leveldir_new->first_level);
2171 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2173 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2175 /* skip level sets without levels (which are probably artwork base sets) */
2177 freeSetupFileHash(setup_file_hash);
2178 free(directory_path);
2186 pushTreeInfo(node_first, leveldir_new);
2188 freeSetupFileHash(setup_file_hash);
2190 if (leveldir_new->level_group)
2192 /* create node to link back to current level directory */
2193 createParentTreeInfoNode(leveldir_new);
2195 /* step into sub-directory and look for more level series */
2196 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2197 leveldir_new, directory_path);
2200 free(directory_path);
2206 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2207 TreeInfo *node_parent,
2208 char *level_directory)
2211 struct dirent *dir_entry;
2212 boolean valid_entry_found = FALSE;
2214 if ((dir = opendir(level_directory)) == NULL)
2216 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2220 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2222 struct stat file_status;
2223 char *directory_name = dir_entry->d_name;
2224 char *directory_path = getPath2(level_directory, directory_name);
2226 /* skip entries for current and parent directory */
2227 if (strEqual(directory_name, ".") ||
2228 strEqual(directory_name, ".."))
2230 free(directory_path);
2234 /* find out if directory entry is itself a directory */
2235 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2236 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2238 free(directory_path);
2242 free(directory_path);
2244 if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
2245 strEqual(directory_name, SOUNDS_DIRECTORY) ||
2246 strEqual(directory_name, MUSIC_DIRECTORY))
2249 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2256 /* special case: top level directory may directly contain "levelinfo.conf" */
2257 if (node_parent == NULL && !valid_entry_found)
2259 /* check if this directory directly contains a file "levelinfo.conf" */
2260 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2261 level_directory, ".");
2264 if (!valid_entry_found)
2265 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2269 boolean AdjustGraphicsForEMC()
2271 boolean settings_changed = FALSE;
2273 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all);
2274 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first);
2276 return settings_changed;
2279 void LoadLevelInfo()
2281 InitUserLevelDirectory(getLoginName());
2283 DrawInitText("Loading level series:", 120, FC_GREEN);
2285 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2286 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);
2297 AdjustGraphicsForEMC();
2299 /* before sorting, the first entries will be from the user directory */
2300 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2302 if (leveldir_first == NULL)
2303 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2305 sortTreeInfo(&leveldir_first);
2308 dumpTreeInfo(leveldir_first, 0);
2312 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2313 TreeInfo *node_parent,
2314 char *base_directory,
2315 char *directory_name, int type)
2317 char *directory_path = getPath2(base_directory, directory_name);
2318 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2319 SetupFileHash *setup_file_hash = NULL;
2320 TreeInfo *artwork_new = NULL;
2323 if (fileExists(filename))
2324 setup_file_hash = loadSetupFileHash(filename);
2326 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2329 struct dirent *dir_entry;
2330 boolean valid_file_found = FALSE;
2332 if ((dir = opendir(directory_path)) != NULL)
2334 while ((dir_entry = readdir(dir)) != NULL)
2336 char *entry_name = dir_entry->d_name;
2338 if (FileIsArtworkType(entry_name, type))
2340 valid_file_found = TRUE;
2348 if (!valid_file_found)
2350 if (!strEqual(directory_name, "."))
2351 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2353 free(directory_path);
2360 artwork_new = newTreeInfo();
2363 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2365 setTreeInfoToDefaults(artwork_new, type);
2367 artwork_new->subdir = getStringCopy(directory_name);
2369 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2372 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2375 /* set all structure fields according to the token/value pairs */
2377 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2378 setSetupInfo(levelinfo_tokens, i,
2379 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2382 if (strEqual(artwork_new->name, ANONYMOUS_NAME))
2383 setString(&artwork_new->name, artwork_new->subdir);
2386 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2389 if (artwork_new->identifier == NULL)
2390 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2392 if (artwork_new->name_sorting == NULL)
2393 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2396 if (node_parent == NULL) /* top level group */
2398 artwork_new->basepath = getStringCopy(base_directory);
2399 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2401 else /* sub level group */
2403 artwork_new->basepath = getStringCopy(node_parent->basepath);
2404 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2407 artwork_new->in_user_dir =
2408 (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)));
2410 /* (may use ".sort_priority" from "setup_file_hash" above) */
2411 artwork_new->color = ARTWORKCOLOR(artwork_new);
2413 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2415 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2417 if (strEqual(artwork_new->subdir, "."))
2419 if (artwork_new->user_defined)
2421 setString(&artwork_new->identifier, "private");
2422 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2426 setString(&artwork_new->identifier, "classic");
2427 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2430 /* set to new values after changing ".sort_priority" */
2431 artwork_new->color = ARTWORKCOLOR(artwork_new);
2433 setString(&artwork_new->class_desc,
2434 getLevelClassDescription(artwork_new));
2438 setString(&artwork_new->identifier, artwork_new->subdir);
2441 setString(&artwork_new->name, artwork_new->identifier);
2442 setString(&artwork_new->name_sorting, artwork_new->name);
2445 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2447 pushTreeInfo(node_first, artwork_new);
2449 freeSetupFileHash(setup_file_hash);
2451 free(directory_path);
2457 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2458 TreeInfo *node_parent,
2459 char *base_directory, int type)
2462 struct dirent *dir_entry;
2463 boolean valid_entry_found = FALSE;
2465 if ((dir = opendir(base_directory)) == NULL)
2467 /* display error if directory is main "options.graphics_directory" etc. */
2468 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2469 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2474 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2476 struct stat file_status;
2477 char *directory_name = dir_entry->d_name;
2478 char *directory_path = getPath2(base_directory, directory_name);
2480 /* skip directory entries for current and parent directory */
2481 if (strEqual(directory_name, ".") ||
2482 strEqual(directory_name, ".."))
2484 free(directory_path);
2488 /* skip directory entries which are not a directory or are not accessible */
2489 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2490 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2492 free(directory_path);
2496 free(directory_path);
2498 /* check if this directory contains artwork with or without config file */
2499 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2501 directory_name, type);
2506 /* check if this directory directly contains artwork itself */
2507 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2508 base_directory, ".",
2510 if (!valid_entry_found)
2511 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2515 static TreeInfo *getDummyArtworkInfo(int type)
2517 /* this is only needed when there is completely no artwork available */
2518 TreeInfo *artwork_new = newTreeInfo();
2520 setTreeInfoToDefaults(artwork_new, type);
2522 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2523 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2524 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2526 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2527 setString(&artwork_new->name, UNDEFINED_FILENAME);
2528 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2533 void LoadArtworkInfo()
2535 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2537 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2538 options.graphics_directory,
2539 TREE_TYPE_GRAPHICS_DIR);
2540 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2541 getUserGraphicsDir(),
2542 TREE_TYPE_GRAPHICS_DIR);
2544 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2545 options.sounds_directory,
2546 TREE_TYPE_SOUNDS_DIR);
2547 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2549 TREE_TYPE_SOUNDS_DIR);
2551 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2552 options.music_directory,
2553 TREE_TYPE_MUSIC_DIR);
2554 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2556 TREE_TYPE_MUSIC_DIR);
2558 if (artwork.gfx_first == NULL)
2559 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2560 if (artwork.snd_first == NULL)
2561 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2562 if (artwork.mus_first == NULL)
2563 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2565 /* before sorting, the first entries will be from the user directory */
2566 artwork.gfx_current =
2567 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2568 if (artwork.gfx_current == NULL)
2569 artwork.gfx_current =
2570 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2571 if (artwork.gfx_current == NULL)
2572 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2574 artwork.snd_current =
2575 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2576 if (artwork.snd_current == NULL)
2577 artwork.snd_current =
2578 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2579 if (artwork.snd_current == NULL)
2580 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2582 artwork.mus_current =
2583 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2584 if (artwork.mus_current == NULL)
2585 artwork.mus_current =
2586 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2587 if (artwork.mus_current == NULL)
2588 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2590 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2591 artwork.snd_current_identifier = artwork.snd_current->identifier;
2592 artwork.mus_current_identifier = artwork.mus_current->identifier;
2595 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2596 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2597 printf("music set == %s\n\n", artwork.mus_current_identifier);
2600 sortTreeInfo(&artwork.gfx_first);
2601 sortTreeInfo(&artwork.snd_first);
2602 sortTreeInfo(&artwork.mus_first);
2605 dumpTreeInfo(artwork.gfx_first, 0);
2606 dumpTreeInfo(artwork.snd_first, 0);
2607 dumpTreeInfo(artwork.mus_first, 0);
2611 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2612 LevelDirTree *level_node)
2614 /* recursively check all level directories for artwork sub-directories */
2618 /* check all tree entries for artwork, but skip parent link entries */
2619 if (!level_node->parent_link)
2621 TreeInfo *topnode_last = *artwork_node;
2622 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2623 ARTWORK_DIRECTORY((*artwork_node)->type));
2625 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2626 (*artwork_node)->type);
2628 if (topnode_last != *artwork_node)
2630 free((*artwork_node)->identifier);
2631 free((*artwork_node)->name);
2632 free((*artwork_node)->name_sorting);
2634 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2635 (*artwork_node)->name = getStringCopy(level_node->name);
2636 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2638 (*artwork_node)->sort_priority = level_node->sort_priority;
2639 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2645 if (level_node->node_group != NULL)
2646 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2648 level_node = level_node->next;
2652 void LoadLevelArtworkInfo()
2654 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2656 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2657 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2658 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2660 /* needed for reloading level artwork not known at ealier stage */
2662 if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set))
2664 artwork.gfx_current =
2665 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2666 if (artwork.gfx_current == NULL)
2667 artwork.gfx_current =
2668 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2669 if (artwork.gfx_current == NULL)
2670 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2673 if (!strEqual(artwork.snd_current_identifier, setup.sounds_set))
2675 artwork.snd_current =
2676 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2677 if (artwork.snd_current == NULL)
2678 artwork.snd_current =
2679 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2680 if (artwork.snd_current == NULL)
2681 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2684 if (!strEqual(artwork.mus_current_identifier, setup.music_set))
2686 artwork.mus_current =
2687 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2688 if (artwork.mus_current == NULL)
2689 artwork.mus_current =
2690 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2691 if (artwork.mus_current == NULL)
2692 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2695 sortTreeInfo(&artwork.gfx_first);
2696 sortTreeInfo(&artwork.snd_first);
2697 sortTreeInfo(&artwork.mus_first);
2700 dumpTreeInfo(artwork.gfx_first, 0);
2701 dumpTreeInfo(artwork.snd_first, 0);
2702 dumpTreeInfo(artwork.mus_first, 0);
2706 static void SaveUserLevelInfo()
2708 LevelDirTree *level_info;
2713 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2715 if (!(file = fopen(filename, MODE_WRITE)))
2717 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2722 level_info = newTreeInfo();
2724 /* always start with reliable default values */
2725 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2727 setString(&level_info->name, getLoginName());
2728 setString(&level_info->author, getRealName());
2729 level_info->levels = 100;
2730 level_info->first_level = 1;
2732 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2734 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2735 getCookie("LEVELINFO")));
2738 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2740 if (i == LEVELINFO_TOKEN_NAME ||
2741 i == LEVELINFO_TOKEN_AUTHOR ||
2742 i == LEVELINFO_TOKEN_LEVELS ||
2743 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2744 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2746 /* just to make things nicer :) */
2747 if (i == LEVELINFO_TOKEN_AUTHOR)
2748 fprintf(file, "\n");
2751 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2755 SetFilePermissions(filename, PERMS_PRIVATE);
2757 freeTreeInfo(level_info);
2761 char *getSetupValue(int type, void *value)
2763 static char value_string[MAX_LINE_LEN];
2771 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2775 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2779 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2783 strcpy(value_string, (*(boolean *)value ? "AGA" : "ECS"));
2787 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2791 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2795 sprintf(value_string, "%d", *(int *)value);
2799 strcpy(value_string, *(char **)value);
2803 value_string[0] = '\0';
2807 if (type & TYPE_GHOSTED)
2808 strcpy(value_string, "n/a");
2810 return value_string;
2813 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2817 static char token_string[MAX_LINE_LEN];
2818 int token_type = token_info[token_nr].type;
2819 void *setup_value = token_info[token_nr].value;
2820 char *token_text = token_info[token_nr].text;
2821 char *value_string = getSetupValue(token_type, setup_value);
2823 /* build complete token string */
2824 sprintf(token_string, "%s%s", prefix, token_text);
2826 /* build setup entry line */
2827 line = getFormattedSetupEntry(token_string, value_string);
2829 if (token_type == TYPE_KEY_X11)
2831 Key key = *(Key *)setup_value;
2832 char *keyname = getKeyNameFromKey(key);
2834 /* add comment, if useful */
2835 if (!strEqual(keyname, "(undefined)") &&
2836 !strEqual(keyname, "(unknown)"))
2838 /* add at least one whitespace */
2840 for (i = strlen(line); i < token_comment_position; i++)
2844 strcat(line, keyname);
2851 void LoadLevelSetup_LastSeries()
2853 /* ----------------------------------------------------------------------- */
2854 /* ~/.<program>/levelsetup.conf */
2855 /* ----------------------------------------------------------------------- */
2857 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2858 SetupFileHash *level_setup_hash = NULL;
2860 /* always start with reliable default values */
2861 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2863 if ((level_setup_hash = loadSetupFileHash(filename)))
2865 char *last_level_series =
2866 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2868 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2870 if (leveldir_current == NULL)
2871 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2873 checkSetupFileHashIdentifier(level_setup_hash, filename,
2874 getCookie("LEVELSETUP"));
2876 freeSetupFileHash(level_setup_hash);
2879 Error(ERR_WARN, "using default setup values");
2884 void SaveLevelSetup_LastSeries()
2886 /* ----------------------------------------------------------------------- */
2887 /* ~/.<program>/levelsetup.conf */
2888 /* ----------------------------------------------------------------------- */
2890 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2891 char *level_subdir = leveldir_current->subdir;
2894 InitUserDataDirectory();
2896 if (!(file = fopen(filename, MODE_WRITE)))
2898 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2903 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2904 getCookie("LEVELSETUP")));
2905 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2910 SetFilePermissions(filename, PERMS_PRIVATE);
2915 static void checkSeriesInfo()
2917 static char *level_directory = NULL;
2919 struct dirent *dir_entry;
2921 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2923 level_directory = getPath2((leveldir_current->in_user_dir ?
2924 getUserLevelDir(NULL) :
2925 options.level_directory),
2926 leveldir_current->fullpath);
2928 if ((dir = opendir(level_directory)) == NULL)
2930 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2934 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2936 if (strlen(dir_entry->d_name) > 4 &&
2937 dir_entry->d_name[3] == '.' &&
2938 strEqual(&dir_entry->d_name[4], LEVELFILE_EXTENSION))
2940 char levelnum_str[4];
2943 strncpy(levelnum_str, dir_entry->d_name, 3);
2944 levelnum_str[3] = '\0';
2946 levelnum_value = atoi(levelnum_str);
2949 if (levelnum_value < leveldir_current->first_level)
2951 Error(ERR_WARN, "additional level %d found", levelnum_value);
2952 leveldir_current->first_level = levelnum_value;
2954 else if (levelnum_value > leveldir_current->last_level)
2956 Error(ERR_WARN, "additional level %d found", levelnum_value);
2957 leveldir_current->last_level = levelnum_value;
2966 void LoadLevelSetup_SeriesInfo()
2969 SetupFileHash *level_setup_hash = NULL;
2970 char *level_subdir = leveldir_current->subdir;
2972 /* always start with reliable default values */
2973 level_nr = leveldir_current->first_level;
2975 checkSeriesInfo(leveldir_current);
2977 /* ----------------------------------------------------------------------- */
2978 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2979 /* ----------------------------------------------------------------------- */
2981 level_subdir = leveldir_current->subdir;
2983 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2985 if ((level_setup_hash = loadSetupFileHash(filename)))
2989 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2993 level_nr = atoi(token_value);
2995 if (level_nr < leveldir_current->first_level)
2996 level_nr = leveldir_current->first_level;
2997 if (level_nr > leveldir_current->last_level)
2998 level_nr = leveldir_current->last_level;
3001 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
3005 int level_nr = atoi(token_value);
3007 if (level_nr < leveldir_current->first_level)
3008 level_nr = leveldir_current->first_level;
3009 if (level_nr > leveldir_current->last_level + 1)
3010 level_nr = leveldir_current->last_level;
3012 if (leveldir_current->user_defined || !leveldir_current->handicap)
3013 level_nr = leveldir_current->last_level;
3015 leveldir_current->handicap_level = level_nr;
3018 checkSetupFileHashIdentifier(level_setup_hash, filename,
3019 getCookie("LEVELSETUP"));
3021 freeSetupFileHash(level_setup_hash);
3024 Error(ERR_WARN, "using default setup values");
3029 void SaveLevelSetup_SeriesInfo()
3032 char *level_subdir = leveldir_current->subdir;
3033 char *level_nr_str = int2str(level_nr, 0);
3034 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3037 /* ----------------------------------------------------------------------- */
3038 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3039 /* ----------------------------------------------------------------------- */
3041 InitLevelSetupDirectory(level_subdir);
3043 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3045 if (!(file = fopen(filename, MODE_WRITE)))
3047 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3052 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3053 getCookie("LEVELSETUP")));
3054 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3056 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3057 handicap_level_str));
3061 SetFilePermissions(filename, PERMS_PRIVATE);