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;
95 static SetupFileHash *level_artwork_info_hash = NULL;
98 /* ------------------------------------------------------------------------- */
100 /* ------------------------------------------------------------------------- */
102 static char *getLevelClassDescription(TreeInfo *ti)
104 int position = ti->sort_priority / 100;
106 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
107 return levelclass_desc[position];
109 return "Unknown Level Class";
112 static char *getUserLevelDir(char *level_subdir)
114 static char *userlevel_dir = NULL;
115 char *data_dir = getUserGameDataDir();
116 char *userlevel_subdir = LEVELS_DIRECTORY;
118 checked_free(userlevel_dir);
120 if (level_subdir != NULL)
121 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
123 userlevel_dir = getPath2(data_dir, userlevel_subdir);
125 return userlevel_dir;
128 static char *getScoreDir(char *level_subdir)
130 static char *score_dir = NULL;
131 char *data_dir = getCommonDataDir();
132 char *score_subdir = SCORES_DIRECTORY;
134 checked_free(score_dir);
136 if (level_subdir != NULL)
137 score_dir = getPath3(data_dir, score_subdir, level_subdir);
139 score_dir = getPath2(data_dir, score_subdir);
144 static char *getLevelSetupDir(char *level_subdir)
146 static char *levelsetup_dir = NULL;
147 char *data_dir = getUserGameDataDir();
148 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
150 checked_free(levelsetup_dir);
152 if (level_subdir != NULL)
153 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
155 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
157 return levelsetup_dir;
160 static char *getLevelDirFromTreeInfo(TreeInfo *node)
162 static char *level_dir = NULL;
165 return options.level_directory;
167 checked_free(level_dir);
169 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
170 options.level_directory), node->fullpath);
175 char *getCurrentLevelDir()
177 return getLevelDirFromTreeInfo(leveldir_current);
180 static char *getTapeDir(char *level_subdir)
182 static char *tape_dir = NULL;
183 char *data_dir = getUserGameDataDir();
184 char *tape_subdir = TAPES_DIRECTORY;
186 checked_free(tape_dir);
188 if (level_subdir != NULL)
189 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
191 tape_dir = getPath2(data_dir, tape_subdir);
196 static char *getSolutionTapeDir()
198 static char *tape_dir = NULL;
199 char *data_dir = getCurrentLevelDir();
200 char *tape_subdir = TAPES_DIRECTORY;
202 checked_free(tape_dir);
204 tape_dir = getPath2(data_dir, tape_subdir);
209 static char *getDefaultGraphicsDir(char *graphics_subdir)
211 static char *graphics_dir = NULL;
213 if (graphics_subdir == NULL)
214 return options.graphics_directory;
216 checked_free(graphics_dir);
218 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
223 static char *getDefaultSoundsDir(char *sounds_subdir)
225 static char *sounds_dir = NULL;
227 if (sounds_subdir == NULL)
228 return options.sounds_directory;
230 checked_free(sounds_dir);
232 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
237 static char *getDefaultMusicDir(char *music_subdir)
239 static char *music_dir = NULL;
241 if (music_subdir == NULL)
242 return options.music_directory;
244 checked_free(music_dir);
246 music_dir = getPath2(options.music_directory, music_subdir);
251 static char *getDefaultArtworkSet(int type)
253 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
254 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
255 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
258 static char *getDefaultArtworkDir(int type)
260 return (type == TREE_TYPE_GRAPHICS_DIR ?
261 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
262 type == TREE_TYPE_SOUNDS_DIR ?
263 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
264 type == TREE_TYPE_MUSIC_DIR ?
265 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
268 static char *getUserGraphicsDir()
270 static char *usergraphics_dir = NULL;
272 if (usergraphics_dir == NULL)
273 usergraphics_dir = getPath2(getUserGameDataDir(), GRAPHICS_DIRECTORY);
275 return usergraphics_dir;
278 static char *getUserSoundsDir()
280 static char *usersounds_dir = NULL;
282 if (usersounds_dir == NULL)
283 usersounds_dir = getPath2(getUserGameDataDir(), SOUNDS_DIRECTORY);
285 return usersounds_dir;
288 static char *getUserMusicDir()
290 static char *usermusic_dir = NULL;
292 if (usermusic_dir == NULL)
293 usermusic_dir = getPath2(getUserGameDataDir(), MUSIC_DIRECTORY);
295 return usermusic_dir;
298 static char *getSetupArtworkDir(TreeInfo *ti)
300 static char *artwork_dir = NULL;
302 checked_free(artwork_dir);
304 artwork_dir = getPath2(ti->basepath, ti->fullpath);
309 char *setLevelArtworkDir(TreeInfo *ti)
311 char **artwork_path_ptr, **artwork_set_ptr;
312 TreeInfo *level_artwork;
314 if (ti == NULL || leveldir_current == NULL)
317 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
318 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
320 checked_free(*artwork_path_ptr);
322 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
323 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
326 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
327 normally result in using the artwork configured in the setup menu. But
328 if an artwork subdirectory exists (which might contain custom artwork
329 or an artwork configuration file), this level artwork must be treated
330 as relative to the default "classic" artwork, not to the artwork that
331 is currently configured in the setup menu. */
333 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
335 checked_free(*artwork_set_ptr);
339 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
340 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
344 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
345 *artwork_set_ptr = NULL;
351 return *artwork_set_ptr;
354 inline static char *getLevelArtworkSet(int type)
356 if (leveldir_current == NULL)
359 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
362 inline static char *getLevelArtworkDir(int type)
364 if (leveldir_current == NULL)
365 return UNDEFINED_FILENAME;
367 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
370 char *getTapeFilename(int nr)
372 static char *filename = NULL;
373 char basename[MAX_FILENAME_LEN];
375 checked_free(filename);
377 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
378 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
383 char *getSolutionTapeFilename(int nr)
385 static char *filename = NULL;
386 char basename[MAX_FILENAME_LEN];
388 checked_free(filename);
390 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
391 filename = getPath2(getSolutionTapeDir(), basename);
396 char *getScoreFilename(int nr)
398 static char *filename = NULL;
399 char basename[MAX_FILENAME_LEN];
401 checked_free(filename);
403 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
404 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
409 char *getSetupFilename()
411 static char *filename = NULL;
413 checked_free(filename);
415 filename = getPath2(getSetupDir(), SETUP_FILENAME);
420 char *getEditorSetupFilename()
422 static char *filename = NULL;
424 checked_free(filename);
425 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
427 if (fileExists(filename))
430 checked_free(filename);
431 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
436 char *getHelpAnimFilename()
438 static char *filename = NULL;
440 checked_free(filename);
442 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
447 char *getHelpTextFilename()
449 static char *filename = NULL;
451 checked_free(filename);
453 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
458 char *getLevelSetInfoFilename()
460 static char *filename = NULL;
475 for (i = 0; basenames[i] != NULL; i++)
477 checked_free(filename);
478 filename = getPath2(getCurrentLevelDir(), basenames[i]);
480 if (fileExists(filename))
487 static char *getCorrectedArtworkBasename(char *basename)
489 char *basename_corrected = basename;
491 #if defined(PLATFORM_MSDOS)
492 if (program.filename_prefix != NULL)
494 int prefix_len = strlen(program.filename_prefix);
496 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
497 basename_corrected = &basename[prefix_len];
499 /* if corrected filename is still longer than standard MS-DOS filename
500 size (8 characters + 1 dot + 3 characters file extension), shorten
501 filename by writing file extension after 8th basename character */
502 if (strlen(basename_corrected) > 8 + 1 + 3)
504 static char *msdos_filename = NULL;
506 checked_free(msdos_filename);
508 msdos_filename = getStringCopy(basename_corrected);
509 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
511 basename_corrected = msdos_filename;
516 return basename_corrected;
519 char *getCustomImageFilename(char *basename)
521 static char *filename = NULL;
522 boolean skip_setup_artwork = FALSE;
524 checked_free(filename);
526 basename = getCorrectedArtworkBasename(basename);
528 if (!setup.override_level_graphics)
530 /* 1st try: look for special artwork in current level series directory */
531 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
532 if (fileExists(filename))
537 /* check if there is special artwork configured in level series config */
538 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
540 /* 2nd try: look for special artwork configured in level series config */
541 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
542 if (fileExists(filename))
547 /* take missing artwork configured in level set config from default */
548 skip_setup_artwork = TRUE;
552 if (!skip_setup_artwork)
554 /* 3rd try: look for special artwork in configured artwork directory */
555 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
556 if (fileExists(filename))
562 /* 4th try: look for default artwork in new default artwork directory */
563 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
564 if (fileExists(filename))
569 /* 5th try: look for default artwork in old default artwork directory */
570 filename = getPath2(options.graphics_directory, basename);
571 if (fileExists(filename))
574 return NULL; /* cannot find specified artwork file anywhere */
577 char *getCustomSoundFilename(char *basename)
579 static char *filename = NULL;
580 boolean skip_setup_artwork = FALSE;
582 checked_free(filename);
584 basename = getCorrectedArtworkBasename(basename);
586 if (!setup.override_level_sounds)
588 /* 1st try: look for special artwork in current level series directory */
589 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
590 if (fileExists(filename))
595 /* check if there is special artwork configured in level series config */
596 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
598 /* 2nd try: look for special artwork configured in level series config */
599 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
600 if (fileExists(filename))
605 /* take missing artwork configured in level set config from default */
606 skip_setup_artwork = TRUE;
610 if (!skip_setup_artwork)
612 /* 3rd try: look for special artwork in configured artwork directory */
613 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
614 if (fileExists(filename))
620 /* 4th try: look for default artwork in new default artwork directory */
621 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
622 if (fileExists(filename))
627 /* 5th try: look for default artwork in old default artwork directory */
628 filename = getPath2(options.sounds_directory, basename);
629 if (fileExists(filename))
632 return NULL; /* cannot find specified artwork file anywhere */
635 char *getCustomMusicFilename(char *basename)
637 static char *filename = NULL;
638 boolean skip_setup_artwork = FALSE;
640 checked_free(filename);
642 basename = getCorrectedArtworkBasename(basename);
644 if (!setup.override_level_music)
646 /* 1st try: look for special artwork in current level series directory */
647 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
648 if (fileExists(filename))
653 /* check if there is special artwork configured in level series config */
654 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
656 /* 2nd try: look for special artwork configured in level series config */
657 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
658 if (fileExists(filename))
663 /* take missing artwork configured in level set config from default */
664 skip_setup_artwork = TRUE;
668 if (!skip_setup_artwork)
670 /* 3rd try: look for special artwork in configured artwork directory */
671 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
672 if (fileExists(filename))
678 /* 4th try: look for default artwork in new default artwork directory */
679 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
680 if (fileExists(filename))
685 /* 5th try: look for default artwork in old default artwork directory */
686 filename = getPath2(options.music_directory, basename);
687 if (fileExists(filename))
690 return NULL; /* cannot find specified artwork file anywhere */
693 char *getCustomArtworkFilename(char *basename, int type)
695 if (type == ARTWORK_TYPE_GRAPHICS)
696 return getCustomImageFilename(basename);
697 else if (type == ARTWORK_TYPE_SOUNDS)
698 return getCustomSoundFilename(basename);
699 else if (type == ARTWORK_TYPE_MUSIC)
700 return getCustomMusicFilename(basename);
702 return UNDEFINED_FILENAME;
705 char *getCustomArtworkConfigFilename(int type)
707 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
710 char *getCustomArtworkLevelConfigFilename(int type)
712 static char *filename = NULL;
714 checked_free(filename);
716 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
721 char *getCustomMusicDirectory(void)
723 static char *directory = NULL;
724 boolean skip_setup_artwork = FALSE;
726 checked_free(directory);
728 if (!setup.override_level_music)
730 /* 1st try: look for special artwork in current level series directory */
731 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
732 if (fileExists(directory))
737 /* check if there is special artwork configured in level series config */
738 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
740 /* 2nd try: look for special artwork configured in level series config */
741 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
742 if (fileExists(directory))
747 /* take missing artwork configured in level set config from default */
748 skip_setup_artwork = TRUE;
752 if (!skip_setup_artwork)
754 /* 3rd try: look for special artwork in configured artwork directory */
755 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
756 if (fileExists(directory))
762 /* 4th try: look for default artwork in new default artwork directory */
763 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
764 if (fileExists(directory))
769 /* 5th try: look for default artwork in old default artwork directory */
770 directory = getStringCopy(options.music_directory);
771 if (fileExists(directory))
774 return NULL; /* cannot find specified artwork file anywhere */
777 void InitTapeDirectory(char *level_subdir)
779 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
780 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
781 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
784 void InitScoreDirectory(char *level_subdir)
786 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
787 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
788 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
791 static void SaveUserLevelInfo();
793 void InitUserLevelDirectory(char *level_subdir)
795 if (!fileExists(getUserLevelDir(level_subdir)))
797 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
798 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
799 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
805 void InitLevelSetupDirectory(char *level_subdir)
807 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
808 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
809 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
813 /* ------------------------------------------------------------------------- */
814 /* some functions to handle lists of level and artwork directories */
815 /* ------------------------------------------------------------------------- */
817 TreeInfo *newTreeInfo()
819 return checked_calloc(sizeof(TreeInfo));
822 TreeInfo *newTreeInfo_setDefaults(int type)
824 TreeInfo *ti = newTreeInfo();
826 setTreeInfoToDefaults(ti, type);
831 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
833 node_new->next = *node_first;
834 *node_first = node_new;
837 int numTreeInfo(TreeInfo *node)
850 boolean validLevelSeries(TreeInfo *node)
852 return (node != NULL && !node->node_group && !node->parent_link);
855 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
860 if (node->node_group) /* enter level group (step down into tree) */
861 return getFirstValidTreeInfoEntry(node->node_group);
862 else if (node->parent_link) /* skip start entry of level group */
864 if (node->next) /* get first real level series entry */
865 return getFirstValidTreeInfoEntry(node->next);
866 else /* leave empty level group and go on */
867 return getFirstValidTreeInfoEntry(node->node_parent->next);
869 else /* this seems to be a regular level series */
873 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
878 if (node->node_parent == NULL) /* top level group */
879 return *node->node_top;
880 else /* sub level group */
881 return node->node_parent->node_group;
884 int numTreeInfoInGroup(TreeInfo *node)
886 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
889 int posTreeInfo(TreeInfo *node)
891 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
896 if (node_cmp == node)
900 node_cmp = node_cmp->next;
906 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
908 TreeInfo *node_default = node;
923 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
925 if (identifier == NULL)
930 if (node->node_group)
932 TreeInfo *node_group;
934 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
939 else if (!node->parent_link)
941 if (strEqual(identifier, node->identifier))
951 TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
952 TreeInfo *node, boolean skip_sets_without_levels)
959 if (!node->parent_link && !node->level_group &&
960 skip_sets_without_levels && node->levels == 0)
961 return cloneTreeNode(node_top, node_parent, node->next,
962 skip_sets_without_levels);
964 node_new = newTreeInfo();
966 *node_new = *node; /* copy complete node */
968 node_new->node_top = node_top; /* correct top node link */
969 node_new->node_parent = node_parent; /* correct parent node link */
971 if (node->level_group)
972 node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group,
973 skip_sets_without_levels);
975 node_new->next = cloneTreeNode(node_top, node_parent, node->next,
976 skip_sets_without_levels);
981 void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
983 TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets);
988 static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
990 boolean settings_changed = FALSE;
994 if (node->graphics_set_ecs && !setup.prefer_aga_graphics &&
995 !strEqual(node->graphics_set, node->graphics_set_ecs))
997 setString(&node->graphics_set, node->graphics_set_ecs);
998 settings_changed = TRUE;
1000 else if (node->graphics_set_aga && setup.prefer_aga_graphics &&
1001 !strEqual(node->graphics_set, node->graphics_set_aga))
1003 setString(&node->graphics_set, node->graphics_set_aga);
1004 settings_changed = TRUE;
1007 if (node->node_group != NULL)
1008 settings_changed |= adjustTreeGraphicsForEMC(node->node_group);
1013 return settings_changed;
1016 void dumpTreeInfo(TreeInfo *node, int depth)
1020 printf("Dumping TreeInfo:\n");
1024 for (i = 0; i < (depth + 1) * 3; i++)
1027 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
1028 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
1030 if (node->node_group != NULL)
1031 dumpTreeInfo(node->node_group, depth + 1);
1037 void sortTreeInfoBySortFunction(TreeInfo **node_first,
1038 int (*compare_function)(const void *,
1041 int num_nodes = numTreeInfo(*node_first);
1042 TreeInfo **sort_array;
1043 TreeInfo *node = *node_first;
1049 /* allocate array for sorting structure pointers */
1050 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1052 /* writing structure pointers to sorting array */
1053 while (i < num_nodes && node) /* double boundary check... */
1055 sort_array[i] = node;
1061 /* sorting the structure pointers in the sorting array */
1062 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1065 /* update the linkage of list elements with the sorted node array */
1066 for (i = 0; i < num_nodes - 1; i++)
1067 sort_array[i]->next = sort_array[i + 1];
1068 sort_array[num_nodes - 1]->next = NULL;
1070 /* update the linkage of the main list anchor pointer */
1071 *node_first = sort_array[0];
1075 /* now recursively sort the level group structures */
1079 if (node->node_group != NULL)
1080 sortTreeInfoBySortFunction(&node->node_group, compare_function);
1086 void sortTreeInfo(TreeInfo **node_first)
1088 sortTreeInfoBySortFunction(node_first, compareTreeInfoEntries);
1092 /* ========================================================================= */
1093 /* some stuff from "files.c" */
1094 /* ========================================================================= */
1096 #if defined(PLATFORM_WIN32)
1098 #define S_IRGRP S_IRUSR
1101 #define S_IROTH S_IRUSR
1104 #define S_IWGRP S_IWUSR
1107 #define S_IWOTH S_IWUSR
1110 #define S_IXGRP S_IXUSR
1113 #define S_IXOTH S_IXUSR
1116 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1121 #endif /* PLATFORM_WIN32 */
1123 /* file permissions for newly written files */
1124 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1125 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1126 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1128 #define MODE_W_PRIVATE (S_IWUSR)
1129 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1130 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1132 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1133 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1135 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1136 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1140 static char *dir = NULL;
1142 #if defined(PLATFORM_WIN32)
1145 dir = checked_malloc(MAX_PATH + 1);
1147 if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir)))
1150 #elif defined(PLATFORM_UNIX)
1153 if ((dir = getenv("HOME")) == NULL)
1157 if ((pwd = getpwuid(getuid())) != NULL)
1158 dir = getStringCopy(pwd->pw_dir);
1170 char *getCommonDataDir(void)
1172 static char *common_data_dir = NULL;
1174 #if defined(PLATFORM_WIN32)
1175 if (common_data_dir == NULL)
1177 char *dir = checked_malloc(MAX_PATH + 1);
1179 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1180 && !strEqual(dir, "")) /* empty for Windows 95/98 */
1181 common_data_dir = getPath2(dir, program.userdata_subdir);
1183 common_data_dir = options.rw_base_directory;
1186 if (common_data_dir == NULL)
1187 common_data_dir = options.rw_base_directory;
1190 return common_data_dir;
1193 char *getPersonalDataDir(void)
1195 static char *personal_data_dir = NULL;
1197 #if defined(PLATFORM_MACOSX)
1198 if (personal_data_dir == NULL)
1199 personal_data_dir = getPath2(getHomeDir(), "Documents");
1201 if (personal_data_dir == NULL)
1202 personal_data_dir = getHomeDir();
1205 return personal_data_dir;
1208 char *getUserGameDataDir(void)
1210 static char *user_game_data_dir = NULL;
1212 if (user_game_data_dir == NULL)
1213 user_game_data_dir = getPath2(getPersonalDataDir(),
1214 program.userdata_subdir);
1216 return user_game_data_dir;
1219 void updateUserGameDataDir()
1221 #if defined(PLATFORM_MACOSX)
1222 char *userdata_dir_old = getPath2(getHomeDir(), program.userdata_subdir_unix);
1223 char *userdata_dir_new = getUserGameDataDir(); /* do not free() this */
1225 /* convert old Unix style game data directory to Mac OS X style, if needed */
1226 if (fileExists(userdata_dir_old) && !fileExists(userdata_dir_new))
1228 if (rename(userdata_dir_old, userdata_dir_new) != 0)
1230 Error(ERR_WARN, "cannot move game data directory '%s' to '%s'",
1231 userdata_dir_old, userdata_dir_new);
1233 /* continue using Unix style data directory -- this should not happen */
1234 program.userdata_path = getPath2(getPersonalDataDir(),
1235 program.userdata_subdir_unix);
1239 free(userdata_dir_old);
1245 return getUserGameDataDir();
1248 static mode_t posix_umask(mode_t mask)
1250 #if defined(PLATFORM_UNIX)
1257 static int posix_mkdir(const char *pathname, mode_t mode)
1259 #if defined(PLATFORM_WIN32)
1260 return mkdir(pathname);
1262 return mkdir(pathname, mode);
1266 void createDirectory(char *dir, char *text, int permission_class)
1268 /* leave "other" permissions in umask untouched, but ensure group parts
1269 of USERDATA_DIR_MODE are not masked */
1270 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1271 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1272 mode_t normal_umask = posix_umask(0);
1273 mode_t group_umask = ~(dir_mode & S_IRWXG);
1274 posix_umask(normal_umask & group_umask);
1276 if (!fileExists(dir))
1277 if (posix_mkdir(dir, dir_mode) != 0)
1278 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1280 posix_umask(normal_umask); /* reset normal umask */
1283 void InitUserDataDirectory()
1285 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
1288 void SetFilePermissions(char *filename, int permission_class)
1290 chmod(filename, (permission_class == PERMS_PRIVATE ?
1291 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1294 char *getCookie(char *file_type)
1296 static char cookie[MAX_COOKIE_LEN + 1];
1298 if (strlen(program.cookie_prefix) + 1 +
1299 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1300 return "[COOKIE ERROR]"; /* should never happen */
1302 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1303 program.cookie_prefix, file_type,
1304 program.version_major, program.version_minor);
1309 int getFileVersionFromCookieString(const char *cookie)
1311 const char *ptr_cookie1, *ptr_cookie2;
1312 const char *pattern1 = "_FILE_VERSION_";
1313 const char *pattern2 = "?.?";
1314 const int len_cookie = strlen(cookie);
1315 const int len_pattern1 = strlen(pattern1);
1316 const int len_pattern2 = strlen(pattern2);
1317 const int len_pattern = len_pattern1 + len_pattern2;
1318 int version_major, version_minor;
1320 if (len_cookie <= len_pattern)
1323 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1324 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1326 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1329 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1330 ptr_cookie2[1] != '.' ||
1331 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1334 version_major = ptr_cookie2[0] - '0';
1335 version_minor = ptr_cookie2[2] - '0';
1337 return VERSION_IDENT(version_major, version_minor, 0, 0);
1340 boolean checkCookieString(const char *cookie, const char *template)
1342 const char *pattern = "_FILE_VERSION_?.?";
1343 const int len_cookie = strlen(cookie);
1344 const int len_template = strlen(template);
1345 const int len_pattern = strlen(pattern);
1347 if (len_cookie != len_template)
1350 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1356 /* ------------------------------------------------------------------------- */
1357 /* setup file list and hash handling functions */
1358 /* ------------------------------------------------------------------------- */
1360 char *getFormattedSetupEntry(char *token, char *value)
1363 static char entry[MAX_LINE_LEN];
1365 /* if value is an empty string, just return token without value */
1369 /* start with the token and some spaces to format output line */
1370 sprintf(entry, "%s:", token);
1371 for (i = strlen(entry); i < token_value_position; i++)
1374 /* continue with the token's value */
1375 strcat(entry, value);
1380 SetupFileList *newSetupFileList(char *token, char *value)
1382 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1384 new->token = getStringCopy(token);
1385 new->value = getStringCopy(value);
1392 void freeSetupFileList(SetupFileList *list)
1397 checked_free(list->token);
1398 checked_free(list->value);
1401 freeSetupFileList(list->next);
1406 char *getListEntry(SetupFileList *list, char *token)
1411 if (strEqual(list->token, token))
1414 return getListEntry(list->next, token);
1417 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1422 if (strEqual(list->token, token))
1424 checked_free(list->value);
1426 list->value = getStringCopy(value);
1430 else if (list->next == NULL)
1431 return (list->next = newSetupFileList(token, value));
1433 return setListEntry(list->next, token, value);
1436 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1441 if (list->next == NULL)
1442 return (list->next = newSetupFileList(token, value));
1444 return addListEntry(list->next, token, value);
1448 static void printSetupFileList(SetupFileList *list)
1453 printf("token: '%s'\n", list->token);
1454 printf("value: '%s'\n", list->value);
1456 printSetupFileList(list->next);
1461 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1462 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1463 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1464 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1466 #define insert_hash_entry hashtable_insert
1467 #define search_hash_entry hashtable_search
1468 #define change_hash_entry hashtable_change
1469 #define remove_hash_entry hashtable_remove
1472 static unsigned int get_hash_from_key(void *key)
1477 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1478 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1479 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1480 it works better than many other constants, prime or not) has never been
1481 adequately explained.
1483 If you just want to have a good hash function, and cannot wait, djb2
1484 is one of the best string hash functions i know. It has excellent
1485 distribution and speed on many different sets of keys and table sizes.
1486 You are not likely to do better with one of the "well known" functions
1487 such as PJW, K&R, etc.
1489 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1492 char *str = (char *)key;
1493 unsigned int hash = 5381;
1496 while ((c = *str++))
1497 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1502 static int keys_are_equal(void *key1, void *key2)
1504 return (strEqual((char *)key1, (char *)key2));
1507 SetupFileHash *newSetupFileHash()
1509 SetupFileHash *new_hash =
1510 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1512 if (new_hash == NULL)
1513 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1518 void freeSetupFileHash(SetupFileHash *hash)
1523 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1526 char *getHashEntry(SetupFileHash *hash, char *token)
1531 return search_hash_entry(hash, token);
1534 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1541 value_copy = getStringCopy(value);
1543 /* change value; if it does not exist, insert it as new */
1544 if (!change_hash_entry(hash, token, value_copy))
1545 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1546 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1549 char *removeHashEntry(SetupFileHash *hash, char *token)
1554 return remove_hash_entry(hash, token);
1558 static void printSetupFileHash(SetupFileHash *hash)
1560 BEGIN_HASH_ITERATION(hash, itr)
1562 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1563 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1565 END_HASH_ITERATION(hash, itr)
1569 static void *loadSetupFileData(char *filename, boolean use_hash)
1571 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1572 char *token, *value, *line_ptr;
1573 void *setup_file_data, *insert_ptr = NULL;
1574 boolean read_continued_line = FALSE;
1577 if (!(file = fopen(filename, MODE_READ)))
1579 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1585 setup_file_data = newSetupFileHash();
1587 insert_ptr = setup_file_data = newSetupFileList("", "");
1591 /* read next line of input file */
1592 if (!fgets(line, MAX_LINE_LEN, file))
1595 /* cut trailing newline or carriage return */
1596 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1597 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1600 if (read_continued_line)
1602 /* cut leading whitespaces from input line */
1603 for (line_ptr = line; *line_ptr; line_ptr++)
1604 if (*line_ptr != ' ' && *line_ptr != '\t')
1607 /* append new line to existing line, if there is enough space */
1608 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1609 strcat(previous_line, line_ptr);
1611 strcpy(line, previous_line); /* copy storage buffer to line */
1613 read_continued_line = FALSE;
1616 /* if the last character is '\', continue at next line */
1617 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1619 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1620 strcpy(previous_line, line); /* copy line to storage buffer */
1622 read_continued_line = TRUE;
1627 /* cut trailing comment from input line */
1628 for (line_ptr = line; *line_ptr; line_ptr++)
1630 if (*line_ptr == '#')
1637 /* cut trailing whitespaces from input line */
1638 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1639 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1642 /* ignore empty lines */
1646 /* cut leading whitespaces from token */
1647 for (token = line; *token; token++)
1648 if (*token != ' ' && *token != '\t')
1651 /* start with empty value as reliable default */
1654 /* find end of token to determine start of value */
1655 for (line_ptr = token; *line_ptr; line_ptr++)
1657 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1659 *line_ptr = '\0'; /* terminate token string */
1660 value = line_ptr + 1; /* set beginning of value */
1666 /* cut leading whitespaces from value */
1667 for (; *value; value++)
1668 if (*value != ' ' && *value != '\t')
1673 value = "true"; /* treat tokens without value as "true" */
1679 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1681 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1689 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1690 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1694 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1695 SetupFileList *first_valid_list_entry = setup_file_list->next;
1697 /* free empty list header */
1698 setup_file_list->next = NULL;
1699 freeSetupFileList(setup_file_list);
1700 setup_file_data = first_valid_list_entry;
1702 if (first_valid_list_entry == NULL)
1703 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1706 return setup_file_data;
1709 void saveSetupFileHash(SetupFileHash *hash, char *filename)
1713 if (!(file = fopen(filename, MODE_WRITE)))
1715 Error(ERR_WARN, "cannot write configuration file '%s'", filename);
1720 BEGIN_HASH_ITERATION(hash, itr)
1722 fprintf(file, "%s\n", getFormattedSetupEntry(HASH_ITERATION_TOKEN(itr),
1723 HASH_ITERATION_VALUE(itr)));
1725 END_HASH_ITERATION(hash, itr)
1730 SetupFileList *loadSetupFileList(char *filename)
1732 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1735 SetupFileHash *loadSetupFileHash(char *filename)
1737 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1740 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1741 char *filename, char *identifier)
1743 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1746 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1747 else if (!checkCookieString(value, identifier))
1748 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1752 /* ========================================================================= */
1753 /* setup file stuff */
1754 /* ========================================================================= */
1756 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1757 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1758 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1760 /* level directory info */
1761 #define LEVELINFO_TOKEN_IDENTIFIER 0
1762 #define LEVELINFO_TOKEN_NAME 1
1763 #define LEVELINFO_TOKEN_NAME_SORTING 2
1764 #define LEVELINFO_TOKEN_AUTHOR 3
1765 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1766 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1767 #define LEVELINFO_TOKEN_LEVELS 6
1768 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1769 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1770 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1771 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1772 #define LEVELINFO_TOKEN_READONLY 11
1773 #define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 12
1774 #define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 13
1775 #define LEVELINFO_TOKEN_GRAPHICS_SET 14
1776 #define LEVELINFO_TOKEN_SOUNDS_SET 15
1777 #define LEVELINFO_TOKEN_MUSIC_SET 16
1778 #define LEVELINFO_TOKEN_FILENAME 17
1779 #define LEVELINFO_TOKEN_FILETYPE 18
1780 #define LEVELINFO_TOKEN_HANDICAP 19
1781 #define LEVELINFO_TOKEN_SKIP_LEVELS 20
1783 #define NUM_LEVELINFO_TOKENS 21
1785 static LevelDirTree ldi;
1787 static struct TokenInfo levelinfo_tokens[] =
1789 /* level directory info */
1790 { TYPE_STRING, &ldi.identifier, "identifier" },
1791 { TYPE_STRING, &ldi.name, "name" },
1792 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1793 { TYPE_STRING, &ldi.author, "author" },
1794 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1795 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1796 { TYPE_INTEGER, &ldi.levels, "levels" },
1797 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1798 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1799 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1800 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1801 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1802 { TYPE_STRING, &ldi.graphics_set_ecs, "graphics_set.ecs" },
1803 { TYPE_STRING, &ldi.graphics_set_aga, "graphics_set.aga" },
1804 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1805 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1806 { TYPE_STRING, &ldi.music_set, "music_set" },
1807 { TYPE_STRING, &ldi.level_filename, "filename" },
1808 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1809 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1810 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1813 static void setTreeInfoToDefaults(TreeInfo *ti, int type)
1817 ti->node_top = (ti->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1818 ti->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1819 ti->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1820 ti->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1823 ti->node_parent = NULL;
1824 ti->node_group = NULL;
1831 ti->fullpath = NULL;
1832 ti->basepath = NULL;
1833 ti->identifier = NULL;
1834 ti->name = getStringCopy(ANONYMOUS_NAME);
1835 ti->name_sorting = NULL;
1836 ti->author = getStringCopy(ANONYMOUS_NAME);
1838 ti->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1839 ti->latest_engine = FALSE; /* default: get from level */
1840 ti->parent_link = FALSE;
1841 ti->in_user_dir = FALSE;
1842 ti->user_defined = FALSE;
1844 ti->class_desc = NULL;
1846 ti->infotext = getStringCopy(TREE_INFOTEXT(ti->type));
1848 if (ti->type == TREE_TYPE_LEVEL_DIR)
1850 ti->imported_from = NULL;
1851 ti->imported_by = NULL;
1853 ti->graphics_set_ecs = NULL;
1854 ti->graphics_set_aga = NULL;
1855 ti->graphics_set = NULL;
1856 ti->sounds_set = NULL;
1857 ti->music_set = NULL;
1858 ti->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1859 ti->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1860 ti->music_path = getStringCopy(UNDEFINED_FILENAME);
1862 ti->level_filename = NULL;
1863 ti->level_filetype = NULL;
1866 ti->first_level = 0;
1868 ti->level_group = FALSE;
1869 ti->handicap_level = 0;
1870 ti->readonly = TRUE;
1871 ti->handicap = TRUE;
1872 ti->skip_levels = FALSE;
1876 static void setTreeInfoToDefaultsFromParent(TreeInfo *ti, TreeInfo *parent)
1880 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1882 setTreeInfoToDefaults(ti, TREE_TYPE_UNDEFINED);
1887 /* copy all values from the parent structure */
1889 ti->type = parent->type;
1891 ti->node_top = parent->node_top;
1892 ti->node_parent = parent;
1893 ti->node_group = NULL;
1900 ti->fullpath = NULL;
1901 ti->basepath = NULL;
1902 ti->identifier = NULL;
1903 ti->name = getStringCopy(ANONYMOUS_NAME);
1904 ti->name_sorting = NULL;
1905 ti->author = getStringCopy(parent->author);
1907 ti->sort_priority = parent->sort_priority;
1908 ti->latest_engine = parent->latest_engine;
1909 ti->parent_link = FALSE;
1910 ti->in_user_dir = parent->in_user_dir;
1911 ti->user_defined = parent->user_defined;
1912 ti->color = parent->color;
1913 ti->class_desc = getStringCopy(parent->class_desc);
1915 ti->infotext = getStringCopy(parent->infotext);
1917 if (ti->type == TREE_TYPE_LEVEL_DIR)
1919 ti->imported_from = getStringCopy(parent->imported_from);
1920 ti->imported_by = getStringCopy(parent->imported_by);
1922 ti->graphics_set_ecs = NULL;
1923 ti->graphics_set_aga = NULL;
1924 ti->graphics_set = NULL;
1925 ti->sounds_set = NULL;
1926 ti->music_set = NULL;
1927 ti->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1928 ti->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1929 ti->music_path = getStringCopy(UNDEFINED_FILENAME);
1931 ti->level_filename = NULL;
1932 ti->level_filetype = NULL;
1935 ti->first_level = 0;
1937 ti->level_group = FALSE;
1938 ti->handicap_level = 0;
1939 ti->readonly = TRUE;
1940 ti->handicap = TRUE;
1941 ti->skip_levels = FALSE;
1945 static void freeTreeInfo(TreeInfo *ti)
1947 checked_free(ti->subdir);
1948 checked_free(ti->fullpath);
1949 checked_free(ti->basepath);
1950 checked_free(ti->identifier);
1952 checked_free(ti->name);
1953 checked_free(ti->name_sorting);
1954 checked_free(ti->author);
1956 checked_free(ti->class_desc);
1958 checked_free(ti->infotext);
1960 if (ti->type == TREE_TYPE_LEVEL_DIR)
1962 checked_free(ti->imported_from);
1963 checked_free(ti->imported_by);
1965 checked_free(ti->graphics_set_ecs);
1966 checked_free(ti->graphics_set_aga);
1967 checked_free(ti->graphics_set);
1968 checked_free(ti->sounds_set);
1969 checked_free(ti->music_set);
1971 checked_free(ti->graphics_path);
1972 checked_free(ti->sounds_path);
1973 checked_free(ti->music_path);
1975 checked_free(ti->level_filename);
1976 checked_free(ti->level_filetype);
1980 void setSetupInfo(struct TokenInfo *token_info,
1981 int token_nr, char *token_value)
1983 int token_type = token_info[token_nr].type;
1984 void *setup_value = token_info[token_nr].value;
1986 if (token_value == NULL)
1989 /* set setup field to corresponding token value */
1994 *(boolean *)setup_value = get_boolean_from_string(token_value);
1998 *(Key *)setup_value = getKeyFromKeyName(token_value);
2002 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
2006 *(int *)setup_value = get_integer_from_string(token_value);
2010 checked_free(*(char **)setup_value);
2011 *(char **)setup_value = getStringCopy(token_value);
2019 static int compareTreeInfoEntries(const void *object1, const void *object2)
2021 const TreeInfo *entry1 = *((TreeInfo **)object1);
2022 const TreeInfo *entry2 = *((TreeInfo **)object2);
2023 int class_sorting1, class_sorting2;
2026 if (entry1->type == TREE_TYPE_LEVEL_DIR)
2028 class_sorting1 = LEVELSORTING(entry1);
2029 class_sorting2 = LEVELSORTING(entry2);
2033 class_sorting1 = ARTWORKSORTING(entry1);
2034 class_sorting2 = ARTWORKSORTING(entry2);
2037 if (entry1->parent_link || entry2->parent_link)
2038 compare_result = (entry1->parent_link ? -1 : +1);
2039 else if (entry1->sort_priority == entry2->sort_priority)
2041 char *name1 = getStringToLower(entry1->name_sorting);
2042 char *name2 = getStringToLower(entry2->name_sorting);
2044 compare_result = strcmp(name1, name2);
2049 else if (class_sorting1 == class_sorting2)
2050 compare_result = entry1->sort_priority - entry2->sort_priority;
2052 compare_result = class_sorting1 - class_sorting2;
2054 return compare_result;
2057 static void createParentTreeInfoNode(TreeInfo *node_parent)
2061 if (node_parent == NULL)
2064 ti_new = newTreeInfo();
2065 setTreeInfoToDefaults(ti_new, node_parent->type);
2067 ti_new->node_parent = node_parent;
2068 ti_new->parent_link = TRUE;
2070 setString(&ti_new->identifier, node_parent->identifier);
2071 setString(&ti_new->name, ".. (parent directory)");
2072 setString(&ti_new->name_sorting, ti_new->name);
2074 setString(&ti_new->subdir, "..");
2075 setString(&ti_new->fullpath, node_parent->fullpath);
2077 ti_new->sort_priority = node_parent->sort_priority;
2078 ti_new->latest_engine = node_parent->latest_engine;
2080 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
2082 pushTreeInfo(&node_parent->node_group, ti_new);
2085 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
2086 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
2088 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
2089 TreeInfo *node_parent,
2090 char *level_directory,
2091 char *directory_name)
2093 char *directory_path = getPath2(level_directory, directory_name);
2094 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
2095 SetupFileHash *setup_file_hash;
2096 LevelDirTree *leveldir_new = NULL;
2099 /* unless debugging, silently ignore directories without "levelinfo.conf" */
2100 if (!options.debug && !fileExists(filename))
2102 free(directory_path);
2108 setup_file_hash = loadSetupFileHash(filename);
2110 if (setup_file_hash == NULL)
2112 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2114 free(directory_path);
2120 leveldir_new = newTreeInfo();
2123 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
2125 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
2127 leveldir_new->subdir = getStringCopy(directory_name);
2129 checkSetupFileHashIdentifier(setup_file_hash, filename,
2130 getCookie("LEVELINFO"));
2132 /* set all structure fields according to the token/value pairs */
2133 ldi = *leveldir_new;
2134 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2135 setSetupInfo(levelinfo_tokens, i,
2136 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2137 *leveldir_new = ldi;
2139 if (strEqual(leveldir_new->name, ANONYMOUS_NAME))
2140 setString(&leveldir_new->name, leveldir_new->subdir);
2142 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2144 if (leveldir_new->identifier == NULL)
2145 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2147 if (leveldir_new->name_sorting == NULL)
2148 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2150 if (node_parent == NULL) /* top level group */
2152 leveldir_new->basepath = getStringCopy(level_directory);
2153 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2155 else /* sub level group */
2157 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2158 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2162 if (leveldir_new->levels < 1)
2163 leveldir_new->levels = 1;
2166 leveldir_new->last_level =
2167 leveldir_new->first_level + leveldir_new->levels - 1;
2169 leveldir_new->in_user_dir =
2170 (!strEqual(leveldir_new->basepath, options.level_directory));
2172 /* adjust some settings if user's private level directory was detected */
2173 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2174 leveldir_new->in_user_dir &&
2175 (strEqual(leveldir_new->subdir, getLoginName()) ||
2176 strEqual(leveldir_new->name, getLoginName()) ||
2177 strEqual(leveldir_new->author, getRealName())))
2179 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2180 leveldir_new->readonly = FALSE;
2183 leveldir_new->user_defined =
2184 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2186 leveldir_new->color = LEVELCOLOR(leveldir_new);
2188 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2190 leveldir_new->handicap_level = /* set handicap to default value */
2191 (leveldir_new->user_defined || !leveldir_new->handicap ?
2192 leveldir_new->last_level : leveldir_new->first_level);
2195 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2197 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2199 /* skip level sets without levels (which are probably artwork base sets) */
2201 freeSetupFileHash(setup_file_hash);
2202 free(directory_path);
2210 pushTreeInfo(node_first, leveldir_new);
2212 freeSetupFileHash(setup_file_hash);
2214 if (leveldir_new->level_group)
2216 /* create node to link back to current level directory */
2217 createParentTreeInfoNode(leveldir_new);
2219 /* step into sub-directory and look for more level series */
2220 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2221 leveldir_new, directory_path);
2224 free(directory_path);
2230 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2231 TreeInfo *node_parent,
2232 char *level_directory)
2235 struct dirent *dir_entry;
2236 boolean valid_entry_found = FALSE;
2238 if ((dir = opendir(level_directory)) == NULL)
2240 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2244 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2246 struct stat file_status;
2247 char *directory_name = dir_entry->d_name;
2248 char *directory_path = getPath2(level_directory, directory_name);
2250 /* skip entries for current and parent directory */
2251 if (strEqual(directory_name, ".") ||
2252 strEqual(directory_name, ".."))
2254 free(directory_path);
2258 /* find out if directory entry is itself a directory */
2259 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2260 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2262 free(directory_path);
2266 free(directory_path);
2268 if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
2269 strEqual(directory_name, SOUNDS_DIRECTORY) ||
2270 strEqual(directory_name, MUSIC_DIRECTORY))
2273 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2280 /* special case: top level directory may directly contain "levelinfo.conf" */
2281 if (node_parent == NULL && !valid_entry_found)
2283 /* check if this directory directly contains a file "levelinfo.conf" */
2284 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2285 level_directory, ".");
2288 if (!valid_entry_found)
2289 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2293 boolean AdjustGraphicsForEMC()
2295 boolean settings_changed = FALSE;
2297 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all);
2298 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first);
2300 return settings_changed;
2303 void LoadLevelInfo()
2305 InitUserLevelDirectory(getLoginName());
2307 DrawInitText("Loading level series:", 120, FC_GREEN);
2309 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2310 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2312 /* after loading all level set information, clone the level directory tree
2313 and remove all level sets without levels (these may still contain artwork
2314 to be offered in the setup menu as "custom artwork", and are therefore
2315 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2316 leveldir_first_all = leveldir_first;
2317 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2319 AdjustGraphicsForEMC();
2321 /* before sorting, the first entries will be from the user directory */
2322 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2324 if (leveldir_first == NULL)
2325 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2327 sortTreeInfo(&leveldir_first);
2330 dumpTreeInfo(leveldir_first, 0);
2334 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2335 TreeInfo *node_parent,
2336 char *base_directory,
2337 char *directory_name, int type)
2339 char *directory_path = getPath2(base_directory, directory_name);
2340 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2341 SetupFileHash *setup_file_hash = NULL;
2342 TreeInfo *artwork_new = NULL;
2345 if (fileExists(filename))
2346 setup_file_hash = loadSetupFileHash(filename);
2348 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2351 struct dirent *dir_entry;
2352 boolean valid_file_found = FALSE;
2354 if ((dir = opendir(directory_path)) != NULL)
2356 while ((dir_entry = readdir(dir)) != NULL)
2358 char *entry_name = dir_entry->d_name;
2360 if (FileIsArtworkType(entry_name, type))
2362 valid_file_found = TRUE;
2370 if (!valid_file_found)
2372 if (!strEqual(directory_name, "."))
2373 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2375 free(directory_path);
2382 artwork_new = newTreeInfo();
2385 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2387 setTreeInfoToDefaults(artwork_new, type);
2389 artwork_new->subdir = getStringCopy(directory_name);
2391 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2394 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2397 /* set all structure fields according to the token/value pairs */
2399 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2400 setSetupInfo(levelinfo_tokens, i,
2401 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2404 if (strEqual(artwork_new->name, ANONYMOUS_NAME))
2405 setString(&artwork_new->name, artwork_new->subdir);
2408 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2411 if (artwork_new->identifier == NULL)
2412 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2414 if (artwork_new->name_sorting == NULL)
2415 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2418 if (node_parent == NULL) /* top level group */
2420 artwork_new->basepath = getStringCopy(base_directory);
2421 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2423 else /* sub level group */
2425 artwork_new->basepath = getStringCopy(node_parent->basepath);
2426 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2429 artwork_new->in_user_dir =
2430 (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)));
2432 /* (may use ".sort_priority" from "setup_file_hash" above) */
2433 artwork_new->color = ARTWORKCOLOR(artwork_new);
2435 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2437 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2439 if (strEqual(artwork_new->subdir, "."))
2441 if (artwork_new->user_defined)
2443 setString(&artwork_new->identifier, "private");
2444 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2448 setString(&artwork_new->identifier, "classic");
2449 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2452 /* set to new values after changing ".sort_priority" */
2453 artwork_new->color = ARTWORKCOLOR(artwork_new);
2455 setString(&artwork_new->class_desc,
2456 getLevelClassDescription(artwork_new));
2460 setString(&artwork_new->identifier, artwork_new->subdir);
2463 setString(&artwork_new->name, artwork_new->identifier);
2464 setString(&artwork_new->name_sorting, artwork_new->name);
2467 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2469 pushTreeInfo(node_first, artwork_new);
2471 freeSetupFileHash(setup_file_hash);
2473 free(directory_path);
2479 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2480 TreeInfo *node_parent,
2481 char *base_directory, int type)
2484 struct dirent *dir_entry;
2485 boolean valid_entry_found = FALSE;
2487 if ((dir = opendir(base_directory)) == NULL)
2489 /* display error if directory is main "options.graphics_directory" etc. */
2490 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2491 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2496 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2498 struct stat file_status;
2499 char *directory_name = dir_entry->d_name;
2500 char *directory_path = getPath2(base_directory, directory_name);
2502 /* skip directory entries for current and parent directory */
2503 if (strEqual(directory_name, ".") ||
2504 strEqual(directory_name, ".."))
2506 free(directory_path);
2510 /* skip directory entries which are not a directory or are not accessible */
2511 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2512 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2514 free(directory_path);
2518 free(directory_path);
2520 /* check if this directory contains artwork with or without config file */
2521 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2523 directory_name, type);
2528 /* check if this directory directly contains artwork itself */
2529 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2530 base_directory, ".",
2532 if (!valid_entry_found)
2533 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2537 static TreeInfo *getDummyArtworkInfo(int type)
2539 /* this is only needed when there is completely no artwork available */
2540 TreeInfo *artwork_new = newTreeInfo();
2542 setTreeInfoToDefaults(artwork_new, type);
2544 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2545 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2546 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2548 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2549 setString(&artwork_new->name, UNDEFINED_FILENAME);
2550 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2555 void LoadArtworkInfo()
2557 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2559 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2560 options.graphics_directory,
2561 TREE_TYPE_GRAPHICS_DIR);
2562 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2563 getUserGraphicsDir(),
2564 TREE_TYPE_GRAPHICS_DIR);
2566 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2567 options.sounds_directory,
2568 TREE_TYPE_SOUNDS_DIR);
2569 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2571 TREE_TYPE_SOUNDS_DIR);
2573 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2574 options.music_directory,
2575 TREE_TYPE_MUSIC_DIR);
2576 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2578 TREE_TYPE_MUSIC_DIR);
2580 if (artwork.gfx_first == NULL)
2581 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2582 if (artwork.snd_first == NULL)
2583 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2584 if (artwork.mus_first == NULL)
2585 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2587 /* before sorting, the first entries will be from the user directory */
2588 artwork.gfx_current =
2589 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2590 if (artwork.gfx_current == NULL)
2591 artwork.gfx_current =
2592 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2593 if (artwork.gfx_current == NULL)
2594 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2596 artwork.snd_current =
2597 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2598 if (artwork.snd_current == NULL)
2599 artwork.snd_current =
2600 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2601 if (artwork.snd_current == NULL)
2602 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2604 artwork.mus_current =
2605 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2606 if (artwork.mus_current == NULL)
2607 artwork.mus_current =
2608 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2609 if (artwork.mus_current == NULL)
2610 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2612 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2613 artwork.snd_current_identifier = artwork.snd_current->identifier;
2614 artwork.mus_current_identifier = artwork.mus_current->identifier;
2617 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2618 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2619 printf("music set == %s\n\n", artwork.mus_current_identifier);
2622 sortTreeInfo(&artwork.gfx_first);
2623 sortTreeInfo(&artwork.snd_first);
2624 sortTreeInfo(&artwork.mus_first);
2627 dumpTreeInfo(artwork.gfx_first, 0);
2628 dumpTreeInfo(artwork.snd_first, 0);
2629 dumpTreeInfo(artwork.mus_first, 0);
2633 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2634 LevelDirTree *level_node)
2636 /* recursively check all level directories for artwork sub-directories */
2640 /* check all tree entries for artwork, but skip parent link entries */
2641 if (!level_node->parent_link)
2643 TreeInfo *topnode_last = *artwork_node;
2644 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2645 ARTWORK_DIRECTORY((*artwork_node)->type));
2647 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2648 (*artwork_node)->type);
2650 if (topnode_last != *artwork_node)
2652 free((*artwork_node)->identifier);
2653 free((*artwork_node)->name);
2654 free((*artwork_node)->name_sorting);
2656 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2657 (*artwork_node)->name = getStringCopy(level_node->name);
2658 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2660 (*artwork_node)->sort_priority = level_node->sort_priority;
2661 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2667 if (level_node->node_group != NULL)
2668 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2670 level_node = level_node->next;
2674 void LoadLevelArtworkInfo()
2676 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2679 if (level_artwork_info_hash == NULL)
2681 char *filename = getPath2(getSetupDir(), "test.conf");
2683 level_artwork_info_hash = loadSetupFileHash(filename);
2685 if (level_artwork_info_hash == NULL)
2686 level_artwork_info_hash = newSetupFileHash();
2692 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2693 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2694 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2696 /* needed for reloading level artwork not known at ealier stage */
2698 if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set))
2700 artwork.gfx_current =
2701 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2702 if (artwork.gfx_current == NULL)
2703 artwork.gfx_current =
2704 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2705 if (artwork.gfx_current == NULL)
2706 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2709 if (!strEqual(artwork.snd_current_identifier, setup.sounds_set))
2711 artwork.snd_current =
2712 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2713 if (artwork.snd_current == NULL)
2714 artwork.snd_current =
2715 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2716 if (artwork.snd_current == NULL)
2717 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2720 if (!strEqual(artwork.mus_current_identifier, setup.music_set))
2722 artwork.mus_current =
2723 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2724 if (artwork.mus_current == NULL)
2725 artwork.mus_current =
2726 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2727 if (artwork.mus_current == NULL)
2728 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2731 sortTreeInfo(&artwork.gfx_first);
2732 sortTreeInfo(&artwork.snd_first);
2733 sortTreeInfo(&artwork.mus_first);
2736 dumpTreeInfo(artwork.gfx_first, 0);
2737 dumpTreeInfo(artwork.snd_first, 0);
2738 dumpTreeInfo(artwork.mus_first, 0);
2742 static void SaveUserLevelInfo()
2744 LevelDirTree *level_info;
2749 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2751 if (!(file = fopen(filename, MODE_WRITE)))
2753 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2758 level_info = newTreeInfo();
2760 /* always start with reliable default values */
2761 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2763 setString(&level_info->name, getLoginName());
2764 setString(&level_info->author, getRealName());
2765 level_info->levels = 100;
2766 level_info->first_level = 1;
2768 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2770 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2771 getCookie("LEVELINFO")));
2774 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2776 if (i == LEVELINFO_TOKEN_NAME ||
2777 i == LEVELINFO_TOKEN_AUTHOR ||
2778 i == LEVELINFO_TOKEN_LEVELS ||
2779 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2780 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2782 /* just to make things nicer :) */
2783 if (i == LEVELINFO_TOKEN_AUTHOR)
2784 fprintf(file, "\n");
2787 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2791 SetFilePermissions(filename, PERMS_PRIVATE);
2793 freeTreeInfo(level_info);
2797 char *getSetupValue(int type, void *value)
2799 static char value_string[MAX_LINE_LEN];
2807 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2811 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2815 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2819 strcpy(value_string, (*(boolean *)value ? "AGA" : "ECS"));
2823 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2827 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2831 sprintf(value_string, "%d", *(int *)value);
2835 strcpy(value_string, *(char **)value);
2839 value_string[0] = '\0';
2843 if (type & TYPE_GHOSTED)
2844 strcpy(value_string, "n/a");
2846 return value_string;
2849 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2853 static char token_string[MAX_LINE_LEN];
2854 int token_type = token_info[token_nr].type;
2855 void *setup_value = token_info[token_nr].value;
2856 char *token_text = token_info[token_nr].text;
2857 char *value_string = getSetupValue(token_type, setup_value);
2859 /* build complete token string */
2860 sprintf(token_string, "%s%s", prefix, token_text);
2862 /* build setup entry line */
2863 line = getFormattedSetupEntry(token_string, value_string);
2865 if (token_type == TYPE_KEY_X11)
2867 Key key = *(Key *)setup_value;
2868 char *keyname = getKeyNameFromKey(key);
2870 /* add comment, if useful */
2871 if (!strEqual(keyname, "(undefined)") &&
2872 !strEqual(keyname, "(unknown)"))
2874 /* add at least one whitespace */
2876 for (i = strlen(line); i < token_comment_position; i++)
2880 strcat(line, keyname);
2887 void LoadLevelSetup_LastSeries()
2889 /* ----------------------------------------------------------------------- */
2890 /* ~/.<program>/levelsetup.conf */
2891 /* ----------------------------------------------------------------------- */
2893 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2894 SetupFileHash *level_setup_hash = NULL;
2896 /* always start with reliable default values */
2897 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2899 if ((level_setup_hash = loadSetupFileHash(filename)))
2901 char *last_level_series =
2902 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2904 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2906 if (leveldir_current == NULL)
2907 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2909 checkSetupFileHashIdentifier(level_setup_hash, filename,
2910 getCookie("LEVELSETUP"));
2912 freeSetupFileHash(level_setup_hash);
2915 Error(ERR_WARN, "using default setup values");
2920 void SaveLevelSetup_LastSeries()
2922 /* ----------------------------------------------------------------------- */
2923 /* ~/.<program>/levelsetup.conf */
2924 /* ----------------------------------------------------------------------- */
2926 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2927 char *level_subdir = leveldir_current->subdir;
2930 InitUserDataDirectory();
2932 if (!(file = fopen(filename, MODE_WRITE)))
2934 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2939 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2940 getCookie("LEVELSETUP")));
2941 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2946 SetFilePermissions(filename, PERMS_PRIVATE);
2951 static void checkSeriesInfo()
2953 static char *level_directory = NULL;
2955 struct dirent *dir_entry;
2957 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2959 level_directory = getPath2((leveldir_current->in_user_dir ?
2960 getUserLevelDir(NULL) :
2961 options.level_directory),
2962 leveldir_current->fullpath);
2964 if ((dir = opendir(level_directory)) == NULL)
2966 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2970 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2972 if (strlen(dir_entry->d_name) > 4 &&
2973 dir_entry->d_name[3] == '.' &&
2974 strEqual(&dir_entry->d_name[4], LEVELFILE_EXTENSION))
2976 char levelnum_str[4];
2979 strncpy(levelnum_str, dir_entry->d_name, 3);
2980 levelnum_str[3] = '\0';
2982 levelnum_value = atoi(levelnum_str);
2985 if (levelnum_value < leveldir_current->first_level)
2987 Error(ERR_WARN, "additional level %d found", levelnum_value);
2988 leveldir_current->first_level = levelnum_value;
2990 else if (levelnum_value > leveldir_current->last_level)
2992 Error(ERR_WARN, "additional level %d found", levelnum_value);
2993 leveldir_current->last_level = levelnum_value;
3002 void LoadLevelSetup_SeriesInfo()
3005 SetupFileHash *level_setup_hash = NULL;
3006 char *level_subdir = leveldir_current->subdir;
3008 /* always start with reliable default values */
3009 level_nr = leveldir_current->first_level;
3011 checkSeriesInfo(leveldir_current);
3013 /* ----------------------------------------------------------------------- */
3014 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3015 /* ----------------------------------------------------------------------- */
3017 level_subdir = leveldir_current->subdir;
3019 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3021 if ((level_setup_hash = loadSetupFileHash(filename)))
3025 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
3029 level_nr = atoi(token_value);
3031 if (level_nr < leveldir_current->first_level)
3032 level_nr = leveldir_current->first_level;
3033 if (level_nr > leveldir_current->last_level)
3034 level_nr = leveldir_current->last_level;
3037 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
3041 int level_nr = atoi(token_value);
3043 if (level_nr < leveldir_current->first_level)
3044 level_nr = leveldir_current->first_level;
3045 if (level_nr > leveldir_current->last_level + 1)
3046 level_nr = leveldir_current->last_level;
3048 if (leveldir_current->user_defined || !leveldir_current->handicap)
3049 level_nr = leveldir_current->last_level;
3051 leveldir_current->handicap_level = level_nr;
3054 checkSetupFileHashIdentifier(level_setup_hash, filename,
3055 getCookie("LEVELSETUP"));
3057 freeSetupFileHash(level_setup_hash);
3060 Error(ERR_WARN, "using default setup values");
3065 void SaveLevelSetup_SeriesInfo()
3068 char *level_subdir = leveldir_current->subdir;
3069 char *level_nr_str = int2str(level_nr, 0);
3070 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3073 /* ----------------------------------------------------------------------- */
3074 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3075 /* ----------------------------------------------------------------------- */
3077 InitLevelSetupDirectory(level_subdir);
3079 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3081 if (!(file = fopen(filename, MODE_WRITE)))
3083 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3088 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3089 getCookie("LEVELSETUP")));
3090 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3092 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3093 handicap_level_str));
3097 SetFilePermissions(filename, PERMS_PRIVATE);