1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
27 #define NUM_LEVELCLASS_DESC 8
29 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
42 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
43 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
44 IS_LEVELCLASS_BD(n) ? FC_YELLOW : \
45 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
46 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
47 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
48 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
49 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
50 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
53 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
54 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
55 IS_LEVELCLASS_BD(n) ? 2 : \
56 IS_LEVELCLASS_EM(n) ? 3 : \
57 IS_LEVELCLASS_SP(n) ? 4 : \
58 IS_LEVELCLASS_DX(n) ? 5 : \
59 IS_LEVELCLASS_SB(n) ? 6 : \
60 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
61 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
64 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
65 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
66 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
67 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
70 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
71 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
76 #define TOKEN_VALUE_POSITION_SHORT 32
77 #define TOKEN_VALUE_POSITION_DEFAULT 40
78 #define TOKEN_COMMENT_POSITION_DEFAULT 60
80 #define MAX_COOKIE_LEN 256
82 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
83 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
86 /* ------------------------------------------------------------------------- */
88 /* ------------------------------------------------------------------------- */
90 static char *getLevelClassDescription(TreeInfo *ldi)
92 int position = ldi->sort_priority / 100;
94 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
95 return levelclass_desc[position];
97 return "Unknown Level Class";
100 static char *getUserLevelDir(char *level_subdir)
102 static char *userlevel_dir = NULL;
103 char *data_dir = getUserDataDir();
104 char *userlevel_subdir = LEVELS_DIRECTORY;
106 checked_free(userlevel_dir);
108 if (level_subdir != NULL)
109 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
111 userlevel_dir = getPath2(data_dir, userlevel_subdir);
113 return userlevel_dir;
116 static char *getScoreDir(char *level_subdir)
118 static char *score_dir = NULL;
119 char *data_dir = getCommonDataDir();
120 char *score_subdir = SCORES_DIRECTORY;
122 checked_free(score_dir);
124 if (level_subdir != NULL)
125 score_dir = getPath3(data_dir, score_subdir, level_subdir);
127 score_dir = getPath2(data_dir, score_subdir);
132 static char *getLevelSetupDir(char *level_subdir)
134 static char *levelsetup_dir = NULL;
135 char *data_dir = getUserDataDir();
136 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
138 checked_free(levelsetup_dir);
140 if (level_subdir != NULL)
141 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
143 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
145 return levelsetup_dir;
148 static char *getLevelDirFromTreeInfo(TreeInfo *node)
150 static char *level_dir = NULL;
153 return options.level_directory;
155 checked_free(level_dir);
157 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
158 options.level_directory), node->fullpath);
163 char *getCurrentLevelDir()
165 return getLevelDirFromTreeInfo(leveldir_current);
168 static char *getTapeDir(char *level_subdir)
170 static char *tape_dir = NULL;
171 char *data_dir = getUserDataDir();
172 char *tape_subdir = TAPES_DIRECTORY;
174 checked_free(tape_dir);
176 if (level_subdir != NULL)
177 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
179 tape_dir = getPath2(data_dir, tape_subdir);
184 static char *getSolutionTapeDir()
186 static char *tape_dir = NULL;
187 char *data_dir = getCurrentLevelDir();
188 char *tape_subdir = TAPES_DIRECTORY;
190 checked_free(tape_dir);
192 tape_dir = getPath2(data_dir, tape_subdir);
197 static char *getDefaultGraphicsDir(char *graphics_subdir)
199 static char *graphics_dir = NULL;
201 if (graphics_subdir == NULL)
202 return options.graphics_directory;
204 checked_free(graphics_dir);
206 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
211 static char *getDefaultSoundsDir(char *sounds_subdir)
213 static char *sounds_dir = NULL;
215 if (sounds_subdir == NULL)
216 return options.sounds_directory;
218 checked_free(sounds_dir);
220 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
225 static char *getDefaultMusicDir(char *music_subdir)
227 static char *music_dir = NULL;
229 if (music_subdir == NULL)
230 return options.music_directory;
232 checked_free(music_dir);
234 music_dir = getPath2(options.music_directory, music_subdir);
239 static char *getDefaultArtworkSet(int type)
241 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
242 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
243 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
246 static char *getDefaultArtworkDir(int type)
248 return (type == TREE_TYPE_GRAPHICS_DIR ?
249 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
250 type == TREE_TYPE_SOUNDS_DIR ?
251 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
252 type == TREE_TYPE_MUSIC_DIR ?
253 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
256 static char *getUserGraphicsDir()
258 static char *usergraphics_dir = NULL;
260 if (usergraphics_dir == NULL)
261 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
263 return usergraphics_dir;
266 static char *getUserSoundsDir()
268 static char *usersounds_dir = NULL;
270 if (usersounds_dir == NULL)
271 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
273 return usersounds_dir;
276 static char *getUserMusicDir()
278 static char *usermusic_dir = NULL;
280 if (usermusic_dir == NULL)
281 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
283 return usermusic_dir;
286 static char *getSetupArtworkDir(TreeInfo *ti)
288 static char *artwork_dir = NULL;
290 checked_free(artwork_dir);
292 artwork_dir = getPath2(ti->basepath, ti->fullpath);
297 char *setLevelArtworkDir(TreeInfo *ti)
299 char **artwork_path_ptr, **artwork_set_ptr;
300 TreeInfo *level_artwork;
302 if (ti == NULL || leveldir_current == NULL)
305 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
306 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
308 checked_free(*artwork_path_ptr);
310 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
311 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
314 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
315 normally result in using the artwork configured in the setup menu. But
316 if an artwork subdirectory exists (which might contain custom artwork
317 or an artwork configuration file), this level artwork must be treated
318 as relative to the default "classic" artwork, not to the artwork that
319 is currently configured in the setup menu. */
321 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
323 checked_free(*artwork_set_ptr);
327 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
328 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
332 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
333 *artwork_set_ptr = NULL;
339 return *artwork_set_ptr;
342 inline static char *getLevelArtworkSet(int type)
344 if (leveldir_current == NULL)
347 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
350 inline static char *getLevelArtworkDir(int type)
352 if (leveldir_current == NULL)
353 return UNDEFINED_FILENAME;
355 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
358 char *getTapeFilename(int nr)
360 static char *filename = NULL;
361 char basename[MAX_FILENAME_LEN];
363 checked_free(filename);
365 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
366 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
371 char *getSolutionTapeFilename(int nr)
373 static char *filename = NULL;
374 char basename[MAX_FILENAME_LEN];
376 checked_free(filename);
378 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
379 filename = getPath2(getSolutionTapeDir(), basename);
384 char *getScoreFilename(int nr)
386 static char *filename = NULL;
387 char basename[MAX_FILENAME_LEN];
389 checked_free(filename);
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 checked_free(filename);
403 filename = getPath2(getSetupDir(), SETUP_FILENAME);
408 char *getEditorSetupFilename()
410 static char *filename = NULL;
412 checked_free(filename);
413 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
415 if (fileExists(filename))
418 checked_free(filename);
419 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
424 char *getHelpAnimFilename()
426 static char *filename = NULL;
428 checked_free(filename);
430 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
435 char *getHelpTextFilename()
437 static char *filename = NULL;
439 checked_free(filename);
441 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
446 char *getLevelSetInfoFilename()
448 static char *filename = NULL;
463 for (i = 0; basenames[i] != NULL; i++)
465 checked_free(filename);
466 filename = getPath2(getCurrentLevelDir(), basenames[i]);
468 if (fileExists(filename))
475 static char *getCorrectedArtworkBasename(char *basename)
477 char *basename_corrected = basename;
479 #if defined(PLATFORM_MSDOS)
480 if (program.filename_prefix != NULL)
482 int prefix_len = strlen(program.filename_prefix);
484 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
485 basename_corrected = &basename[prefix_len];
487 /* if corrected filename is still longer than standard MS-DOS filename
488 size (8 characters + 1 dot + 3 characters file extension), shorten
489 filename by writing file extension after 8th basename character */
490 if (strlen(basename_corrected) > 8 + 1 + 3)
492 static char *msdos_filename = NULL;
494 checked_free(msdos_filename);
496 msdos_filename = getStringCopy(basename_corrected);
497 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
499 basename_corrected = msdos_filename;
504 return basename_corrected;
507 char *getCustomImageFilename(char *basename)
509 static char *filename = NULL;
510 boolean skip_setup_artwork = FALSE;
512 checked_free(filename);
514 basename = getCorrectedArtworkBasename(basename);
516 if (!setup.override_level_graphics)
519 /* try special ECS graphics */
520 filename = getPath3(getCurrentLevelDir(), GRAPHICS_ECS_DIRECTORY, basename);
521 if (fileExists(filename) && !setup.prefer_aga_graphics)
526 /* try special AGA graphics */
527 filename = getPath3(getCurrentLevelDir(), GRAPHICS_AGA_DIRECTORY, basename);
528 if (fileExists(filename) && setup.prefer_aga_graphics)
534 /* 1st try: look for special artwork in current level series directory */
535 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
536 if (fileExists(filename))
542 if (leveldir_current)
543 printf("::: A -> '%s' [%s]\n", leveldir_current->graphics_set,
544 leveldir_current->subdir);
547 /* check if there is special artwork configured in level series config */
548 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
551 printf("::: B -> '%s' ---------> '%s'\n",
552 getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS),
553 leveldir_current->graphics_path);
554 /* -> getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS)); */
557 /* 2nd try: look for special artwork configured in level series config */
558 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
559 if (fileExists(filename))
564 /* take missing artwork configured in level set config from default */
565 skip_setup_artwork = TRUE;
569 if (!skip_setup_artwork)
571 /* 3rd try: look for special artwork in configured artwork directory */
572 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
573 if (fileExists(filename))
579 /* 4th try: look for default artwork in new default artwork directory */
580 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
581 if (fileExists(filename))
586 /* 5th try: look for default artwork in old default artwork directory */
587 filename = getPath2(options.graphics_directory, basename);
588 if (fileExists(filename))
591 return NULL; /* cannot find specified artwork file anywhere */
594 char *getCustomSoundFilename(char *basename)
596 static char *filename = NULL;
597 boolean skip_setup_artwork = FALSE;
599 checked_free(filename);
601 basename = getCorrectedArtworkBasename(basename);
603 if (!setup.override_level_sounds)
605 /* 1st try: look for special artwork in current level series directory */
606 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
607 if (fileExists(filename))
612 /* check if there is special artwork configured in level series config */
613 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
615 /* 2nd try: look for special artwork configured in level series config */
616 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
617 if (fileExists(filename))
622 /* take missing artwork configured in level set config from default */
623 skip_setup_artwork = TRUE;
627 if (!skip_setup_artwork)
629 /* 3rd try: look for special artwork in configured artwork directory */
630 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
631 if (fileExists(filename))
637 /* 4th try: look for default artwork in new default artwork directory */
638 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
639 if (fileExists(filename))
644 /* 5th try: look for default artwork in old default artwork directory */
645 filename = getPath2(options.sounds_directory, basename);
646 if (fileExists(filename))
649 return NULL; /* cannot find specified artwork file anywhere */
652 char *getCustomMusicFilename(char *basename)
654 static char *filename = NULL;
655 boolean skip_setup_artwork = FALSE;
657 checked_free(filename);
659 basename = getCorrectedArtworkBasename(basename);
661 if (!setup.override_level_music)
663 /* 1st try: look for special artwork in current level series directory */
664 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
665 if (fileExists(filename))
670 /* check if there is special artwork configured in level series config */
671 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
673 /* 2nd try: look for special artwork configured in level series config */
674 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
675 if (fileExists(filename))
680 /* take missing artwork configured in level set config from default */
681 skip_setup_artwork = TRUE;
685 if (!skip_setup_artwork)
687 /* 3rd try: look for special artwork in configured artwork directory */
688 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
689 if (fileExists(filename))
695 /* 4th try: look for default artwork in new default artwork directory */
696 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
697 if (fileExists(filename))
702 /* 5th try: look for default artwork in old default artwork directory */
703 filename = getPath2(options.music_directory, basename);
704 if (fileExists(filename))
707 return NULL; /* cannot find specified artwork file anywhere */
710 char *getCustomArtworkFilename(char *basename, int type)
712 if (type == ARTWORK_TYPE_GRAPHICS)
713 return getCustomImageFilename(basename);
714 else if (type == ARTWORK_TYPE_SOUNDS)
715 return getCustomSoundFilename(basename);
716 else if (type == ARTWORK_TYPE_MUSIC)
717 return getCustomMusicFilename(basename);
719 return UNDEFINED_FILENAME;
722 char *getCustomArtworkConfigFilename(int type)
724 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
727 char *getCustomArtworkLevelConfigFilename(int type)
729 static char *filename = NULL;
731 checked_free(filename);
733 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
738 char *getCustomMusicDirectory(void)
740 static char *directory = NULL;
741 boolean skip_setup_artwork = FALSE;
743 checked_free(directory);
745 if (!setup.override_level_music)
747 /* 1st try: look for special artwork in current level series directory */
748 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
749 if (fileExists(directory))
754 /* check if there is special artwork configured in level series config */
755 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
757 /* 2nd try: look for special artwork configured in level series config */
758 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
759 if (fileExists(directory))
764 /* take missing artwork configured in level set config from default */
765 skip_setup_artwork = TRUE;
769 if (!skip_setup_artwork)
771 /* 3rd try: look for special artwork in configured artwork directory */
772 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
773 if (fileExists(directory))
779 /* 4th try: look for default artwork in new default artwork directory */
780 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
781 if (fileExists(directory))
786 /* 5th try: look for default artwork in old default artwork directory */
787 directory = getStringCopy(options.music_directory);
788 if (fileExists(directory))
791 return NULL; /* cannot find specified artwork file anywhere */
794 void InitTapeDirectory(char *level_subdir)
796 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
797 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
798 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
801 void InitScoreDirectory(char *level_subdir)
803 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
804 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
805 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
808 static void SaveUserLevelInfo();
810 void InitUserLevelDirectory(char *level_subdir)
812 if (!fileExists(getUserLevelDir(level_subdir)))
814 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
815 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
816 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
822 void InitLevelSetupDirectory(char *level_subdir)
824 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
825 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
826 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
830 /* ------------------------------------------------------------------------- */
831 /* some functions to handle lists of level and artwork directories */
832 /* ------------------------------------------------------------------------- */
834 TreeInfo *newTreeInfo()
836 return checked_calloc(sizeof(TreeInfo));
839 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
841 node_new->next = *node_first;
842 *node_first = node_new;
845 int numTreeInfo(TreeInfo *node)
858 boolean validLevelSeries(TreeInfo *node)
860 return (node != NULL && !node->node_group && !node->parent_link);
863 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
868 if (node->node_group) /* enter level group (step down into tree) */
869 return getFirstValidTreeInfoEntry(node->node_group);
870 else if (node->parent_link) /* skip start entry of level group */
872 if (node->next) /* get first real level series entry */
873 return getFirstValidTreeInfoEntry(node->next);
874 else /* leave empty level group and go on */
875 return getFirstValidTreeInfoEntry(node->node_parent->next);
877 else /* this seems to be a regular level series */
881 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
886 if (node->node_parent == NULL) /* top level group */
887 return *node->node_top;
888 else /* sub level group */
889 return node->node_parent->node_group;
892 int numTreeInfoInGroup(TreeInfo *node)
894 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
897 int posTreeInfo(TreeInfo *node)
899 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
904 if (node_cmp == node)
908 node_cmp = node_cmp->next;
914 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
916 TreeInfo *node_default = node;
931 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
933 if (identifier == NULL)
938 if (node->node_group)
940 TreeInfo *node_group;
942 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
947 else if (!node->parent_link)
949 if (strcmp(identifier, node->identifier) == 0)
959 TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
960 TreeInfo *node, boolean skip_sets_without_levels)
967 if (!node->parent_link && !node->level_group &&
968 skip_sets_without_levels && node->levels == 0)
969 return cloneTreeNode(node_top, node_parent, node->next,
970 skip_sets_without_levels);
972 node_new = newTreeInfo();
974 *node_new = *node; /* copy complete node */
976 node_new->node_top = node_top; /* correct top node link */
977 node_new->node_parent = node_parent; /* correct parent node link */
979 if (node->level_group)
980 node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group,
981 skip_sets_without_levels);
983 node_new->next = cloneTreeNode(node_top, node_parent, node->next,
984 skip_sets_without_levels);
989 void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
991 TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets);
996 static boolean adjustTreeGraphics(TreeInfo *node)
998 boolean settings_changed = FALSE;
1003 if (node->graphics_ecs_set && !setup.prefer_aga_graphics)
1005 setString(&node->graphics_set, node->graphics_ecs_set);
1007 printf("::: setting graphics for set '%s' to '%s' [ECS]\n",
1008 node->subdir, node->graphics_set);
1011 settings_changed = TRUE;
1013 else if (node->graphics_aga_set && setup.prefer_aga_graphics)
1015 setString(&node->graphics_set, node->graphics_aga_set);
1017 printf("::: setting graphics for set '%s' to '%s' [AGA]\n",
1018 node->subdir, node->graphics_set);
1021 settings_changed = TRUE;
1023 else if (node->graphics_set == NULL)
1026 printf("::: cannot set graphics_set for set '%s'\n", node->subdir);
1030 if (node->graphics_ecs_set)
1031 printf("::: SET '%s': found ECS set '%s'\n",
1032 node->subdir, node->graphics_ecs_set);
1034 if (node->graphics_aga_set)
1035 printf("::: SET '%s': found AGA set '%s'\n",
1036 node->subdir, node->graphics_aga_set);
1039 if (node->node_group != NULL)
1040 settings_changed |= adjustTreeGraphics(node->node_group);
1045 return settings_changed;
1048 void dumpTreeInfo(TreeInfo *node, int depth)
1052 printf("Dumping TreeInfo:\n");
1056 for (i = 0; i < (depth + 1) * 3; i++)
1059 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
1060 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
1062 if (node->node_group != NULL)
1063 dumpTreeInfo(node->node_group, depth + 1);
1069 void sortTreeInfo(TreeInfo **node_first,
1070 int (*compare_function)(const void *, const void *))
1072 int num_nodes = numTreeInfo(*node_first);
1073 TreeInfo **sort_array;
1074 TreeInfo *node = *node_first;
1080 /* allocate array for sorting structure pointers */
1081 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1083 /* writing structure pointers to sorting array */
1084 while (i < num_nodes && node) /* double boundary check... */
1086 sort_array[i] = node;
1092 /* sorting the structure pointers in the sorting array */
1093 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1096 /* update the linkage of list elements with the sorted node array */
1097 for (i = 0; i < num_nodes - 1; i++)
1098 sort_array[i]->next = sort_array[i + 1];
1099 sort_array[num_nodes - 1]->next = NULL;
1101 /* update the linkage of the main list anchor pointer */
1102 *node_first = sort_array[0];
1106 /* now recursively sort the level group structures */
1110 if (node->node_group != NULL)
1111 sortTreeInfo(&node->node_group, compare_function);
1118 /* ========================================================================= */
1119 /* some stuff from "files.c" */
1120 /* ========================================================================= */
1122 #if defined(PLATFORM_WIN32)
1124 #define S_IRGRP S_IRUSR
1127 #define S_IROTH S_IRUSR
1130 #define S_IWGRP S_IWUSR
1133 #define S_IWOTH S_IWUSR
1136 #define S_IXGRP S_IXUSR
1139 #define S_IXOTH S_IXUSR
1142 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1147 #endif /* PLATFORM_WIN32 */
1149 /* file permissions for newly written files */
1150 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1151 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1152 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1154 #define MODE_W_PRIVATE (S_IWUSR)
1155 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1156 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1158 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1159 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1161 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1162 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1164 char *getUserDataDir(void)
1166 static char *userdata_dir = NULL;
1168 if (userdata_dir == NULL)
1169 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
1171 return userdata_dir;
1174 char *getCommonDataDir(void)
1176 static char *common_data_dir = NULL;
1178 #if defined(PLATFORM_WIN32)
1179 if (common_data_dir == NULL)
1181 char *dir = checked_malloc(MAX_PATH + 1);
1183 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1184 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
1185 common_data_dir = getPath2(dir, program.userdata_directory);
1187 common_data_dir = options.rw_base_directory;
1190 if (common_data_dir == NULL)
1191 common_data_dir = options.rw_base_directory;
1194 return common_data_dir;
1199 return getUserDataDir();
1202 static mode_t posix_umask(mode_t mask)
1204 #if defined(PLATFORM_UNIX)
1211 static int posix_mkdir(const char *pathname, mode_t mode)
1213 #if defined(PLATFORM_WIN32)
1214 return mkdir(pathname);
1216 return mkdir(pathname, mode);
1220 void createDirectory(char *dir, char *text, int permission_class)
1222 /* leave "other" permissions in umask untouched, but ensure group parts
1223 of USERDATA_DIR_MODE are not masked */
1224 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1225 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1226 mode_t normal_umask = posix_umask(0);
1227 mode_t group_umask = ~(dir_mode & S_IRWXG);
1228 posix_umask(normal_umask & group_umask);
1230 if (!fileExists(dir))
1231 if (posix_mkdir(dir, dir_mode) != 0)
1232 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1234 posix_umask(normal_umask); /* reset normal umask */
1237 void InitUserDataDirectory()
1239 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
1242 void SetFilePermissions(char *filename, int permission_class)
1244 chmod(filename, (permission_class == PERMS_PRIVATE ?
1245 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1248 char *getCookie(char *file_type)
1250 static char cookie[MAX_COOKIE_LEN + 1];
1252 if (strlen(program.cookie_prefix) + 1 +
1253 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1254 return "[COOKIE ERROR]"; /* should never happen */
1256 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1257 program.cookie_prefix, file_type,
1258 program.version_major, program.version_minor);
1263 int getFileVersionFromCookieString(const char *cookie)
1265 const char *ptr_cookie1, *ptr_cookie2;
1266 const char *pattern1 = "_FILE_VERSION_";
1267 const char *pattern2 = "?.?";
1268 const int len_cookie = strlen(cookie);
1269 const int len_pattern1 = strlen(pattern1);
1270 const int len_pattern2 = strlen(pattern2);
1271 const int len_pattern = len_pattern1 + len_pattern2;
1272 int version_major, version_minor;
1274 if (len_cookie <= len_pattern)
1277 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1278 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1280 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1283 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1284 ptr_cookie2[1] != '.' ||
1285 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1288 version_major = ptr_cookie2[0] - '0';
1289 version_minor = ptr_cookie2[2] - '0';
1291 return VERSION_IDENT(version_major, version_minor, 0, 0);
1294 boolean checkCookieString(const char *cookie, const char *template)
1296 const char *pattern = "_FILE_VERSION_?.?";
1297 const int len_cookie = strlen(cookie);
1298 const int len_template = strlen(template);
1299 const int len_pattern = strlen(pattern);
1301 if (len_cookie != len_template)
1304 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1310 /* ------------------------------------------------------------------------- */
1311 /* setup file list and hash handling functions */
1312 /* ------------------------------------------------------------------------- */
1314 char *getFormattedSetupEntry(char *token, char *value)
1317 static char entry[MAX_LINE_LEN];
1319 /* if value is an empty string, just return token without value */
1323 /* start with the token and some spaces to format output line */
1324 sprintf(entry, "%s:", token);
1325 for (i = strlen(entry); i < token_value_position; i++)
1328 /* continue with the token's value */
1329 strcat(entry, value);
1334 SetupFileList *newSetupFileList(char *token, char *value)
1336 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1338 new->token = getStringCopy(token);
1339 new->value = getStringCopy(value);
1346 void freeSetupFileList(SetupFileList *list)
1351 checked_free(list->token);
1352 checked_free(list->value);
1355 freeSetupFileList(list->next);
1360 char *getListEntry(SetupFileList *list, char *token)
1365 if (strcmp(list->token, token) == 0)
1368 return getListEntry(list->next, token);
1371 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1376 if (strcmp(list->token, token) == 0)
1378 checked_free(list->value);
1380 list->value = getStringCopy(value);
1384 else if (list->next == NULL)
1385 return (list->next = newSetupFileList(token, value));
1387 return setListEntry(list->next, token, value);
1390 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1395 if (list->next == NULL)
1396 return (list->next = newSetupFileList(token, value));
1398 return addListEntry(list->next, token, value);
1402 static void printSetupFileList(SetupFileList *list)
1407 printf("token: '%s'\n", list->token);
1408 printf("value: '%s'\n", list->value);
1410 printSetupFileList(list->next);
1415 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1416 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1417 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1418 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1420 #define insert_hash_entry hashtable_insert
1421 #define search_hash_entry hashtable_search
1422 #define change_hash_entry hashtable_change
1423 #define remove_hash_entry hashtable_remove
1426 static unsigned int get_hash_from_key(void *key)
1431 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1432 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1433 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1434 it works better than many other constants, prime or not) has never been
1435 adequately explained.
1437 If you just want to have a good hash function, and cannot wait, djb2
1438 is one of the best string hash functions i know. It has excellent
1439 distribution and speed on many different sets of keys and table sizes.
1440 You are not likely to do better with one of the "well known" functions
1441 such as PJW, K&R, etc.
1443 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1446 char *str = (char *)key;
1447 unsigned int hash = 5381;
1450 while ((c = *str++))
1451 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1456 static int keys_are_equal(void *key1, void *key2)
1458 return (strcmp((char *)key1, (char *)key2) == 0);
1461 SetupFileHash *newSetupFileHash()
1463 SetupFileHash *new_hash =
1464 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1466 if (new_hash == NULL)
1467 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1472 void freeSetupFileHash(SetupFileHash *hash)
1477 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1480 char *getHashEntry(SetupFileHash *hash, char *token)
1485 return search_hash_entry(hash, token);
1488 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1495 value_copy = getStringCopy(value);
1497 /* change value; if it does not exist, insert it as new */
1498 if (!change_hash_entry(hash, token, value_copy))
1499 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1500 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1503 char *removeHashEntry(SetupFileHash *hash, char *token)
1508 return remove_hash_entry(hash, token);
1512 static void printSetupFileHash(SetupFileHash *hash)
1514 BEGIN_HASH_ITERATION(hash, itr)
1516 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1517 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1519 END_HASH_ITERATION(hash, itr)
1523 static void *loadSetupFileData(char *filename, boolean use_hash)
1525 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1526 char *token, *value, *line_ptr;
1527 void *setup_file_data, *insert_ptr = NULL;
1528 boolean read_continued_line = FALSE;
1531 if (!(file = fopen(filename, MODE_READ)))
1533 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1539 setup_file_data = newSetupFileHash();
1541 insert_ptr = setup_file_data = newSetupFileList("", "");
1545 /* read next line of input file */
1546 if (!fgets(line, MAX_LINE_LEN, file))
1549 /* cut trailing newline or carriage return */
1550 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1551 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1554 if (read_continued_line)
1556 /* cut leading whitespaces from input line */
1557 for (line_ptr = line; *line_ptr; line_ptr++)
1558 if (*line_ptr != ' ' && *line_ptr != '\t')
1561 /* append new line to existing line, if there is enough space */
1562 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1563 strcat(previous_line, line_ptr);
1565 strcpy(line, previous_line); /* copy storage buffer to line */
1567 read_continued_line = FALSE;
1570 /* if the last character is '\', continue at next line */
1571 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1573 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1574 strcpy(previous_line, line); /* copy line to storage buffer */
1576 read_continued_line = TRUE;
1581 /* cut trailing comment from input line */
1582 for (line_ptr = line; *line_ptr; line_ptr++)
1584 if (*line_ptr == '#')
1591 /* cut trailing whitespaces from input line */
1592 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1593 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1596 /* ignore empty lines */
1600 /* cut leading whitespaces from token */
1601 for (token = line; *token; token++)
1602 if (*token != ' ' && *token != '\t')
1605 /* start with empty value as reliable default */
1608 /* find end of token to determine start of value */
1609 for (line_ptr = token; *line_ptr; line_ptr++)
1611 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1613 *line_ptr = '\0'; /* terminate token string */
1614 value = line_ptr + 1; /* set beginning of value */
1620 /* cut leading whitespaces from value */
1621 for (; *value; value++)
1622 if (*value != ' ' && *value != '\t')
1627 value = "true"; /* treat tokens without value as "true" */
1633 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1635 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1643 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1644 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1648 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1649 SetupFileList *first_valid_list_entry = setup_file_list->next;
1651 /* free empty list header */
1652 setup_file_list->next = NULL;
1653 freeSetupFileList(setup_file_list);
1654 setup_file_data = first_valid_list_entry;
1656 if (first_valid_list_entry == NULL)
1657 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1660 return setup_file_data;
1663 SetupFileList *loadSetupFileList(char *filename)
1665 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1668 SetupFileHash *loadSetupFileHash(char *filename)
1670 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1673 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1674 char *filename, char *identifier)
1676 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1679 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1680 else if (!checkCookieString(value, identifier))
1681 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1685 /* ========================================================================= */
1686 /* setup file stuff */
1687 /* ========================================================================= */
1689 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1690 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1691 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1693 /* level directory info */
1694 #define LEVELINFO_TOKEN_IDENTIFIER 0
1695 #define LEVELINFO_TOKEN_NAME 1
1696 #define LEVELINFO_TOKEN_NAME_SORTING 2
1697 #define LEVELINFO_TOKEN_AUTHOR 3
1698 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1699 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1700 #define LEVELINFO_TOKEN_LEVELS 6
1701 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1702 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1703 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1704 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1705 #define LEVELINFO_TOKEN_READONLY 11
1706 #define LEVELINFO_TOKEN_GRAPHICS_ECS_SET 12
1707 #define LEVELINFO_TOKEN_GRAPHICS_AGA_SET 13
1708 #define LEVELINFO_TOKEN_GRAPHICS_SET 14
1709 #define LEVELINFO_TOKEN_SOUNDS_SET 15
1710 #define LEVELINFO_TOKEN_MUSIC_SET 16
1711 #define LEVELINFO_TOKEN_FILENAME 17
1712 #define LEVELINFO_TOKEN_FILETYPE 18
1713 #define LEVELINFO_TOKEN_HANDICAP 19
1714 #define LEVELINFO_TOKEN_SKIP_LEVELS 20
1716 #define NUM_LEVELINFO_TOKENS 21
1718 static LevelDirTree ldi;
1720 static struct TokenInfo levelinfo_tokens[] =
1722 /* level directory info */
1723 { TYPE_STRING, &ldi.identifier, "identifier" },
1724 { TYPE_STRING, &ldi.name, "name" },
1725 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1726 { TYPE_STRING, &ldi.author, "author" },
1727 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1728 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1729 { TYPE_INTEGER, &ldi.levels, "levels" },
1730 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1731 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1732 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1733 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1734 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1735 { TYPE_STRING, &ldi.graphics_ecs_set, "graphics_ecs_set" },
1736 { TYPE_STRING, &ldi.graphics_aga_set, "graphics_aga_set" },
1737 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1738 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1739 { TYPE_STRING, &ldi.music_set, "music_set" },
1740 { TYPE_STRING, &ldi.level_filename, "filename" },
1741 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1742 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1743 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1746 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1750 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1751 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1752 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1753 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1756 ldi->node_parent = NULL;
1757 ldi->node_group = NULL;
1761 ldi->cl_cursor = -1;
1764 ldi->fullpath = NULL;
1765 ldi->basepath = NULL;
1766 ldi->identifier = NULL;
1767 ldi->name = getStringCopy(ANONYMOUS_NAME);
1768 ldi->name_sorting = NULL;
1769 ldi->author = getStringCopy(ANONYMOUS_NAME);
1771 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1772 ldi->latest_engine = FALSE; /* default: get from level */
1773 ldi->parent_link = FALSE;
1774 ldi->in_user_dir = FALSE;
1775 ldi->user_defined = FALSE;
1777 ldi->class_desc = NULL;
1779 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1781 ldi->imported_from = NULL;
1782 ldi->imported_by = NULL;
1784 ldi->graphics_ecs_set = NULL;
1785 ldi->graphics_aga_set = NULL;
1786 ldi->graphics_set = NULL;
1787 ldi->sounds_set = NULL;
1788 ldi->music_set = NULL;
1789 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1790 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1791 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1793 ldi->level_filename = NULL;
1794 ldi->level_filetype = NULL;
1797 ldi->first_level = 0;
1798 ldi->last_level = 0;
1799 ldi->level_group = FALSE;
1800 ldi->handicap_level = 0;
1801 ldi->readonly = TRUE;
1802 ldi->handicap = TRUE;
1803 ldi->skip_levels = FALSE;
1807 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1811 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1813 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1818 /* copy all values from the parent structure */
1820 ldi->type = parent->type;
1822 ldi->node_top = parent->node_top;
1823 ldi->node_parent = parent;
1824 ldi->node_group = NULL;
1828 ldi->cl_cursor = -1;
1831 ldi->fullpath = NULL;
1832 ldi->basepath = NULL;
1833 ldi->identifier = NULL;
1834 ldi->name = getStringCopy(ANONYMOUS_NAME);
1835 ldi->name_sorting = NULL;
1836 ldi->author = getStringCopy(parent->author);
1838 ldi->sort_priority = parent->sort_priority;
1839 ldi->latest_engine = parent->latest_engine;
1840 ldi->parent_link = FALSE;
1841 ldi->in_user_dir = parent->in_user_dir;
1842 ldi->user_defined = parent->user_defined;
1843 ldi->color = parent->color;
1844 ldi->class_desc = getStringCopy(parent->class_desc);
1846 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1848 ldi->imported_from = getStringCopy(parent->imported_from);
1849 ldi->imported_by = getStringCopy(parent->imported_by);
1851 ldi->graphics_ecs_set = NULL;
1852 ldi->graphics_aga_set = NULL;
1853 ldi->graphics_set = NULL;
1854 ldi->sounds_set = NULL;
1855 ldi->music_set = NULL;
1856 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1857 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1858 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1860 ldi->level_filename = NULL;
1861 ldi->level_filetype = NULL;
1864 ldi->first_level = 0;
1865 ldi->last_level = 0;
1866 ldi->level_group = FALSE;
1867 ldi->handicap_level = 0;
1868 ldi->readonly = TRUE;
1869 ldi->handicap = TRUE;
1870 ldi->skip_levels = FALSE;
1874 static void freeTreeInfo(TreeInfo *ldi)
1876 checked_free(ldi->subdir);
1877 checked_free(ldi->fullpath);
1878 checked_free(ldi->basepath);
1879 checked_free(ldi->identifier);
1881 checked_free(ldi->name);
1882 checked_free(ldi->name_sorting);
1883 checked_free(ldi->author);
1885 checked_free(ldi->class_desc);
1887 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1889 checked_free(ldi->imported_from);
1890 checked_free(ldi->imported_by);
1892 checked_free(ldi->graphics_ecs_set);
1893 checked_free(ldi->graphics_aga_set);
1894 checked_free(ldi->graphics_set);
1895 checked_free(ldi->sounds_set);
1896 checked_free(ldi->music_set);
1898 checked_free(ldi->graphics_path);
1899 checked_free(ldi->sounds_path);
1900 checked_free(ldi->music_path);
1902 checked_free(ldi->level_filename);
1903 checked_free(ldi->level_filetype);
1907 void setSetupInfo(struct TokenInfo *token_info,
1908 int token_nr, char *token_value)
1910 int token_type = token_info[token_nr].type;
1911 void *setup_value = token_info[token_nr].value;
1913 if (token_value == NULL)
1916 /* set setup field to corresponding token value */
1921 *(boolean *)setup_value = get_boolean_from_string(token_value);
1925 *(Key *)setup_value = getKeyFromKeyName(token_value);
1929 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1933 *(int *)setup_value = get_integer_from_string(token_value);
1937 checked_free(*(char **)setup_value);
1938 *(char **)setup_value = getStringCopy(token_value);
1946 static int compareTreeInfoEntries(const void *object1, const void *object2)
1948 const TreeInfo *entry1 = *((TreeInfo **)object1);
1949 const TreeInfo *entry2 = *((TreeInfo **)object2);
1950 int class_sorting1, class_sorting2;
1953 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1955 class_sorting1 = LEVELSORTING(entry1);
1956 class_sorting2 = LEVELSORTING(entry2);
1960 class_sorting1 = ARTWORKSORTING(entry1);
1961 class_sorting2 = ARTWORKSORTING(entry2);
1964 if (entry1->parent_link || entry2->parent_link)
1965 compare_result = (entry1->parent_link ? -1 : +1);
1966 else if (entry1->sort_priority == entry2->sort_priority)
1968 char *name1 = getStringToLower(entry1->name_sorting);
1969 char *name2 = getStringToLower(entry2->name_sorting);
1971 compare_result = strcmp(name1, name2);
1976 else if (class_sorting1 == class_sorting2)
1977 compare_result = entry1->sort_priority - entry2->sort_priority;
1979 compare_result = class_sorting1 - class_sorting2;
1981 return compare_result;
1984 static void createParentTreeInfoNode(TreeInfo *node_parent)
1988 if (node_parent == NULL)
1991 ti_new = newTreeInfo();
1992 setTreeInfoToDefaults(ti_new, node_parent->type);
1994 ti_new->node_parent = node_parent;
1995 ti_new->parent_link = TRUE;
1997 setString(&ti_new->identifier, node_parent->identifier);
1998 setString(&ti_new->name, ".. (parent directory)");
1999 setString(&ti_new->name_sorting, ti_new->name);
2001 setString(&ti_new->subdir, "..");
2002 setString(&ti_new->fullpath, node_parent->fullpath);
2004 ti_new->sort_priority = node_parent->sort_priority;
2005 ti_new->latest_engine = node_parent->latest_engine;
2007 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
2009 pushTreeInfo(&node_parent->node_group, ti_new);
2012 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
2013 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
2015 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
2016 TreeInfo *node_parent,
2017 char *level_directory,
2018 char *directory_name)
2020 char *directory_path = getPath2(level_directory, directory_name);
2021 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
2022 SetupFileHash *setup_file_hash;
2023 LevelDirTree *leveldir_new = NULL;
2026 /* unless debugging, silently ignore directories without "levelinfo.conf" */
2027 if (!options.debug && !fileExists(filename))
2029 free(directory_path);
2035 setup_file_hash = loadSetupFileHash(filename);
2037 if (setup_file_hash == NULL)
2039 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2041 free(directory_path);
2047 leveldir_new = newTreeInfo();
2050 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
2052 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
2054 leveldir_new->subdir = getStringCopy(directory_name);
2056 checkSetupFileHashIdentifier(setup_file_hash, filename,
2057 getCookie("LEVELINFO"));
2059 /* set all structure fields according to the token/value pairs */
2060 ldi = *leveldir_new;
2061 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2062 setSetupInfo(levelinfo_tokens, i,
2063 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2064 *leveldir_new = ldi;
2066 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
2067 setString(&leveldir_new->name, leveldir_new->subdir);
2069 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2071 if (leveldir_new->identifier == NULL)
2072 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2074 if (leveldir_new->name_sorting == NULL)
2075 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2077 if (node_parent == NULL) /* top level group */
2079 leveldir_new->basepath = getStringCopy(level_directory);
2080 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2082 else /* sub level group */
2084 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2085 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2089 if (leveldir_new->levels < 1)
2090 leveldir_new->levels = 1;
2093 leveldir_new->last_level =
2094 leveldir_new->first_level + leveldir_new->levels - 1;
2096 leveldir_new->in_user_dir =
2097 (strcmp(leveldir_new->basepath, options.level_directory) != 0);
2099 /* adjust some settings if user's private level directory was detected */
2100 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2101 leveldir_new->in_user_dir &&
2102 (strcmp(leveldir_new->subdir, getLoginName()) == 0 ||
2103 strcmp(leveldir_new->name, getLoginName()) == 0 ||
2104 strcmp(leveldir_new->author, getRealName()) == 0))
2106 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2107 leveldir_new->readonly = FALSE;
2110 leveldir_new->user_defined =
2111 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2113 leveldir_new->color = LEVELCOLOR(leveldir_new);
2115 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2117 leveldir_new->handicap_level = /* set handicap to default value */
2118 (leveldir_new->user_defined || !leveldir_new->handicap ?
2119 leveldir_new->last_level : leveldir_new->first_level);
2122 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2124 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2126 /* skip level sets without levels (which are probably artwork base sets) */
2128 freeSetupFileHash(setup_file_hash);
2129 free(directory_path);
2137 pushTreeInfo(node_first, leveldir_new);
2139 freeSetupFileHash(setup_file_hash);
2141 if (leveldir_new->level_group)
2143 /* create node to link back to current level directory */
2144 createParentTreeInfoNode(leveldir_new);
2146 /* step into sub-directory and look for more level series */
2147 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2148 leveldir_new, directory_path);
2151 free(directory_path);
2157 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2158 TreeInfo *node_parent,
2159 char *level_directory)
2162 struct dirent *dir_entry;
2163 boolean valid_entry_found = FALSE;
2165 if ((dir = opendir(level_directory)) == NULL)
2167 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2171 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2173 struct stat file_status;
2174 char *directory_name = dir_entry->d_name;
2175 char *directory_path = getPath2(level_directory, directory_name);
2177 /* skip entries for current and parent directory */
2178 if (strcmp(directory_name, ".") == 0 ||
2179 strcmp(directory_name, "..") == 0)
2181 free(directory_path);
2185 /* find out if directory entry is itself a directory */
2186 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2187 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2189 free(directory_path);
2193 free(directory_path);
2195 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
2196 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
2197 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
2200 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2207 /* special case: top level directory may directly contain "levelinfo.conf" */
2208 if (node_parent == NULL && !valid_entry_found)
2210 /* check if this directory directly contains a file "levelinfo.conf" */
2211 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2212 level_directory, ".");
2215 if (!valid_entry_found)
2216 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2220 boolean AdjustGraphicsForEMC()
2222 boolean settings_changed = FALSE;
2225 printf("::: AdjustGraphicsForEMC()\n");
2227 settings_changed |= adjustTreeGraphics(leveldir_first_all);
2228 settings_changed |= adjustTreeGraphics(leveldir_first);
2230 if (leveldir_current)
2231 printf("::: X -> '%s'\n", leveldir_current->graphics_set);
2233 printf("::: X (leveldir_current == NULL)\n");
2236 return settings_changed;
2239 void LoadLevelInfo()
2241 InitUserLevelDirectory(getLoginName());
2243 DrawInitText("Loading level series:", 120, FC_GREEN);
2245 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2246 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2249 /* after loading all level set information, clone the level directory tree
2250 and remove all level sets without levels (these may still contain artwork
2251 to be offered in the setup menu as "custom artwork", and are therefore
2252 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2253 leveldir_first_all = leveldir_first;
2254 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2257 AdjustGraphicsForEMC();
2259 /* before sorting, the first entries will be from the user directory */
2260 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2262 if (leveldir_first == NULL)
2263 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2265 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2268 dumpTreeInfo(leveldir_first, 0);
2272 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2273 TreeInfo *node_parent,
2274 char *base_directory,
2275 char *directory_name, int type)
2277 char *directory_path = getPath2(base_directory, directory_name);
2278 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2279 SetupFileHash *setup_file_hash = NULL;
2280 TreeInfo *artwork_new = NULL;
2284 printf("::: CHECKING FOR CONFIG FILE '%s'\n", filename);
2287 if (fileExists(filename))
2288 setup_file_hash = loadSetupFileHash(filename);
2290 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2293 struct dirent *dir_entry;
2294 boolean valid_file_found = FALSE;
2296 if ((dir = opendir(directory_path)) != NULL)
2298 while ((dir_entry = readdir(dir)) != NULL)
2300 char *entry_name = dir_entry->d_name;
2302 if (FileIsArtworkType(entry_name, type))
2304 valid_file_found = TRUE;
2312 if (!valid_file_found)
2314 if (strcmp(directory_name, ".") != 0)
2315 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2317 free(directory_path);
2324 artwork_new = newTreeInfo();
2327 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2329 setTreeInfoToDefaults(artwork_new, type);
2331 artwork_new->subdir = getStringCopy(directory_name);
2333 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2336 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2339 /* set all structure fields according to the token/value pairs */
2341 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2342 setSetupInfo(levelinfo_tokens, i,
2343 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2346 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
2347 setString(&artwork_new->name, artwork_new->subdir);
2350 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2353 if (artwork_new->identifier == NULL)
2354 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2356 if (artwork_new->name_sorting == NULL)
2357 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2360 if (node_parent == NULL) /* top level group */
2362 artwork_new->basepath = getStringCopy(base_directory);
2363 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2365 else /* sub level group */
2367 artwork_new->basepath = getStringCopy(node_parent->basepath);
2368 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2371 artwork_new->in_user_dir =
2372 (strcmp(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)) != 0);
2374 /* (may use ".sort_priority" from "setup_file_hash" above) */
2375 artwork_new->color = ARTWORKCOLOR(artwork_new);
2377 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2379 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2381 if (strcmp(artwork_new->subdir, ".") == 0)
2383 if (artwork_new->user_defined)
2385 setString(&artwork_new->identifier, "private");
2386 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2390 setString(&artwork_new->identifier, "classic");
2391 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2394 /* set to new values after changing ".sort_priority" */
2395 artwork_new->color = ARTWORKCOLOR(artwork_new);
2397 setString(&artwork_new->class_desc,
2398 getLevelClassDescription(artwork_new));
2402 setString(&artwork_new->identifier, artwork_new->subdir);
2405 setString(&artwork_new->name, artwork_new->identifier);
2406 setString(&artwork_new->name_sorting, artwork_new->name);
2409 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2411 pushTreeInfo(node_first, artwork_new);
2413 freeSetupFileHash(setup_file_hash);
2415 free(directory_path);
2421 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2422 TreeInfo *node_parent,
2423 char *base_directory, int type)
2426 struct dirent *dir_entry;
2427 boolean valid_entry_found = FALSE;
2430 printf("::: CHECKING BASE DIR '%s'\n", base_directory);
2433 if ((dir = opendir(base_directory)) == NULL)
2435 /* display error if directory is main "options.graphics_directory" etc. */
2436 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2437 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2442 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2444 struct stat file_status;
2445 char *directory_name = dir_entry->d_name;
2446 char *directory_path = getPath2(base_directory, directory_name);
2448 /* skip directory entries for current and parent directory */
2449 if (strcmp(directory_name, ".") == 0 ||
2450 strcmp(directory_name, "..") == 0)
2452 free(directory_path);
2456 /* skip directory entries which are not a directory or are not accessible */
2457 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2458 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2460 free(directory_path);
2464 free(directory_path);
2466 /* check if this directory contains artwork with or without config file */
2467 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2469 directory_name, type);
2474 /* check if this directory directly contains artwork itself */
2475 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2476 base_directory, ".",
2478 if (!valid_entry_found)
2479 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2483 static TreeInfo *getDummyArtworkInfo(int type)
2485 /* this is only needed when there is completely no artwork available */
2486 TreeInfo *artwork_new = newTreeInfo();
2488 setTreeInfoToDefaults(artwork_new, type);
2490 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2491 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2492 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2494 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2495 setString(&artwork_new->name, UNDEFINED_FILENAME);
2496 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2501 void LoadArtworkInfo()
2503 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2505 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2506 options.graphics_directory,
2507 TREE_TYPE_GRAPHICS_DIR);
2508 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2509 getUserGraphicsDir(),
2510 TREE_TYPE_GRAPHICS_DIR);
2512 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2513 options.sounds_directory,
2514 TREE_TYPE_SOUNDS_DIR);
2515 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2517 TREE_TYPE_SOUNDS_DIR);
2519 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2520 options.music_directory,
2521 TREE_TYPE_MUSIC_DIR);
2522 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2524 TREE_TYPE_MUSIC_DIR);
2526 if (artwork.gfx_first == NULL)
2527 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2528 if (artwork.snd_first == NULL)
2529 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2530 if (artwork.mus_first == NULL)
2531 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2533 /* before sorting, the first entries will be from the user directory */
2534 artwork.gfx_current =
2535 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2536 if (artwork.gfx_current == NULL)
2537 artwork.gfx_current =
2538 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2539 if (artwork.gfx_current == NULL)
2540 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2542 artwork.snd_current =
2543 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2544 if (artwork.snd_current == NULL)
2545 artwork.snd_current =
2546 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2547 if (artwork.snd_current == NULL)
2548 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2550 artwork.mus_current =
2551 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2552 if (artwork.mus_current == NULL)
2553 artwork.mus_current =
2554 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2555 if (artwork.mus_current == NULL)
2556 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2558 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2559 artwork.snd_current_identifier = artwork.snd_current->identifier;
2560 artwork.mus_current_identifier = artwork.mus_current->identifier;
2563 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2564 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2565 printf("music set == %s\n\n", artwork.mus_current_identifier);
2568 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2569 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2570 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2573 dumpTreeInfo(artwork.gfx_first, 0);
2574 dumpTreeInfo(artwork.snd_first, 0);
2575 dumpTreeInfo(artwork.mus_first, 0);
2579 void LoadArtworkInfoFromLevelNode(ArtworkDirTree **artwork_node,
2580 LevelDirTree *level_node,
2581 char *artwork_directory)
2583 TreeInfo *topnode_last = *artwork_node;
2584 char *path = getPath2(getLevelDirFromTreeInfo(level_node), artwork_directory);
2587 printf("::: CHECKING '%s' ...\n", path);
2590 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,(*artwork_node)->type);
2592 if (topnode_last != *artwork_node)
2594 free((*artwork_node)->identifier);
2595 free((*artwork_node)->name);
2596 free((*artwork_node)->name_sorting);
2598 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2599 (*artwork_node)->name = getStringCopy(level_node->name);
2600 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2602 (*artwork_node)->sort_priority = level_node->sort_priority;
2603 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2609 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2610 LevelDirTree *level_node)
2612 /* recursively check all level directories for artwork sub-directories */
2616 /* check all tree entries for artwork, but skip parent link entries */
2617 if (!level_node->parent_link)
2625 artwork_type_dirs[] =
2627 { ARTWORK_TYPE_GRAPHICS, GRAPHICS_DIRECTORY },
2628 { ARTWORK_TYPE_GRAPHICS, GRAPHICS_ECS_DIRECTORY },
2629 { ARTWORK_TYPE_GRAPHICS, GRAPHICS_AGA_DIRECTORY },
2630 { ARTWORK_TYPE_SOUNDS, SOUNDS_DIRECTORY },
2631 { ARTWORK_TYPE_MUSIC, MUSIC_DIRECTORY },
2636 for (i = 0; artwork_type_dirs[i].type != -1; i++)
2637 if ((*artwork_node)->type == artwork_type_dirs[i].type)
2638 LoadArtworkInfoFromLevelNode(artwork_node, level_node,
2639 artwork_type_dirs[i].dir);
2641 TreeInfo *topnode_last = *artwork_node;
2642 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2643 ARTWORK_DIRECTORY((*artwork_node)->type));
2645 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2646 (*artwork_node)->type);
2648 if (topnode_last != *artwork_node)
2650 free((*artwork_node)->identifier);
2651 free((*artwork_node)->name);
2652 free((*artwork_node)->name_sorting);
2654 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2655 (*artwork_node)->name = getStringCopy(level_node->name);
2656 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2658 (*artwork_node)->sort_priority = level_node->sort_priority;
2659 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2666 if (level_node->node_group != NULL)
2667 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2669 level_node = level_node->next;
2673 void LoadLevelArtworkInfo()
2675 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2677 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2678 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2679 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2681 /* needed for reloading level artwork not known at ealier stage */
2683 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2685 artwork.gfx_current =
2686 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2687 if (artwork.gfx_current == NULL)
2688 artwork.gfx_current =
2689 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2690 if (artwork.gfx_current == NULL)
2691 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2694 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2696 artwork.snd_current =
2697 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2698 if (artwork.snd_current == NULL)
2699 artwork.snd_current =
2700 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2701 if (artwork.snd_current == NULL)
2702 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2705 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2707 artwork.mus_current =
2708 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2709 if (artwork.mus_current == NULL)
2710 artwork.mus_current =
2711 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2712 if (artwork.mus_current == NULL)
2713 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2716 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2717 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2718 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2721 dumpTreeInfo(artwork.gfx_first, 0);
2722 dumpTreeInfo(artwork.snd_first, 0);
2723 dumpTreeInfo(artwork.mus_first, 0);
2727 static void SaveUserLevelInfo()
2729 LevelDirTree *level_info;
2734 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2736 if (!(file = fopen(filename, MODE_WRITE)))
2738 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2743 level_info = newTreeInfo();
2745 /* always start with reliable default values */
2746 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2748 setString(&level_info->name, getLoginName());
2749 setString(&level_info->author, getRealName());
2750 level_info->levels = 100;
2751 level_info->first_level = 1;
2753 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2755 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2756 getCookie("LEVELINFO")));
2759 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2761 if (i == LEVELINFO_TOKEN_NAME ||
2762 i == LEVELINFO_TOKEN_AUTHOR ||
2763 i == LEVELINFO_TOKEN_LEVELS ||
2764 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2765 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2767 /* just to make things nicer :) */
2768 if (i == LEVELINFO_TOKEN_AUTHOR)
2769 fprintf(file, "\n");
2772 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2776 SetFilePermissions(filename, PERMS_PRIVATE);
2778 freeTreeInfo(level_info);
2782 char *getSetupValue(int type, void *value)
2784 static char value_string[MAX_LINE_LEN];
2792 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2796 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2800 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2804 strcpy(value_string, (*(boolean *)value ? "AGA" : "ECS"));
2808 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2812 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2816 sprintf(value_string, "%d", *(int *)value);
2820 strcpy(value_string, *(char **)value);
2824 value_string[0] = '\0';
2828 if (type & TYPE_GHOSTED)
2829 strcpy(value_string, "n/a");
2831 return value_string;
2834 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2838 static char token_string[MAX_LINE_LEN];
2839 int token_type = token_info[token_nr].type;
2840 void *setup_value = token_info[token_nr].value;
2841 char *token_text = token_info[token_nr].text;
2842 char *value_string = getSetupValue(token_type, setup_value);
2844 /* build complete token string */
2845 sprintf(token_string, "%s%s", prefix, token_text);
2847 /* build setup entry line */
2848 line = getFormattedSetupEntry(token_string, value_string);
2850 if (token_type == TYPE_KEY_X11)
2852 Key key = *(Key *)setup_value;
2853 char *keyname = getKeyNameFromKey(key);
2855 /* add comment, if useful */
2856 if (strcmp(keyname, "(undefined)") != 0 &&
2857 strcmp(keyname, "(unknown)") != 0)
2859 /* add at least one whitespace */
2861 for (i = strlen(line); i < token_comment_position; i++)
2865 strcat(line, keyname);
2872 void LoadLevelSetup_LastSeries()
2874 /* ----------------------------------------------------------------------- */
2875 /* ~/.<program>/levelsetup.conf */
2876 /* ----------------------------------------------------------------------- */
2878 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2879 SetupFileHash *level_setup_hash = NULL;
2881 /* always start with reliable default values */
2882 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2884 if ((level_setup_hash = loadSetupFileHash(filename)))
2886 char *last_level_series =
2887 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2889 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2891 if (leveldir_current == NULL)
2892 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2894 checkSetupFileHashIdentifier(level_setup_hash, filename,
2895 getCookie("LEVELSETUP"));
2897 freeSetupFileHash(level_setup_hash);
2900 Error(ERR_WARN, "using default setup values");
2905 void SaveLevelSetup_LastSeries()
2907 /* ----------------------------------------------------------------------- */
2908 /* ~/.<program>/levelsetup.conf */
2909 /* ----------------------------------------------------------------------- */
2911 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2912 char *level_subdir = leveldir_current->subdir;
2915 InitUserDataDirectory();
2917 if (!(file = fopen(filename, MODE_WRITE)))
2919 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2924 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2925 getCookie("LEVELSETUP")));
2926 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2931 SetFilePermissions(filename, PERMS_PRIVATE);
2936 static void checkSeriesInfo()
2938 static char *level_directory = NULL;
2940 struct dirent *dir_entry;
2942 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2944 level_directory = getPath2((leveldir_current->in_user_dir ?
2945 getUserLevelDir(NULL) :
2946 options.level_directory),
2947 leveldir_current->fullpath);
2949 if ((dir = opendir(level_directory)) == NULL)
2951 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2955 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2957 if (strlen(dir_entry->d_name) > 4 &&
2958 dir_entry->d_name[3] == '.' &&
2959 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2961 char levelnum_str[4];
2964 strncpy(levelnum_str, dir_entry->d_name, 3);
2965 levelnum_str[3] = '\0';
2967 levelnum_value = atoi(levelnum_str);
2970 if (levelnum_value < leveldir_current->first_level)
2972 Error(ERR_WARN, "additional level %d found", levelnum_value);
2973 leveldir_current->first_level = levelnum_value;
2975 else if (levelnum_value > leveldir_current->last_level)
2977 Error(ERR_WARN, "additional level %d found", levelnum_value);
2978 leveldir_current->last_level = levelnum_value;
2987 void LoadLevelSetup_SeriesInfo()
2990 SetupFileHash *level_setup_hash = NULL;
2991 char *level_subdir = leveldir_current->subdir;
2993 /* always start with reliable default values */
2994 level_nr = leveldir_current->first_level;
2996 checkSeriesInfo(leveldir_current);
2998 /* ----------------------------------------------------------------------- */
2999 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3000 /* ----------------------------------------------------------------------- */
3002 level_subdir = leveldir_current->subdir;
3004 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3006 if ((level_setup_hash = loadSetupFileHash(filename)))
3010 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
3014 level_nr = atoi(token_value);
3016 if (level_nr < leveldir_current->first_level)
3017 level_nr = leveldir_current->first_level;
3018 if (level_nr > leveldir_current->last_level)
3019 level_nr = leveldir_current->last_level;
3022 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
3026 int level_nr = atoi(token_value);
3028 if (level_nr < leveldir_current->first_level)
3029 level_nr = leveldir_current->first_level;
3030 if (level_nr > leveldir_current->last_level + 1)
3031 level_nr = leveldir_current->last_level;
3033 if (leveldir_current->user_defined || !leveldir_current->handicap)
3034 level_nr = leveldir_current->last_level;
3036 leveldir_current->handicap_level = level_nr;
3039 checkSetupFileHashIdentifier(level_setup_hash, filename,
3040 getCookie("LEVELSETUP"));
3042 freeSetupFileHash(level_setup_hash);
3045 Error(ERR_WARN, "using default setup values");
3050 void SaveLevelSetup_SeriesInfo()
3053 char *level_subdir = leveldir_current->subdir;
3054 char *level_nr_str = int2str(level_nr, 0);
3055 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3058 /* ----------------------------------------------------------------------- */
3059 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3060 /* ----------------------------------------------------------------------- */
3062 InitLevelSetupDirectory(level_subdir);
3064 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3066 if (!(file = fopen(filename, MODE_WRITE)))
3068 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3073 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3074 getCookie("LEVELSETUP")));
3075 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3077 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3078 handicap_level_str));
3082 SetFilePermissions(filename, PERMS_PRIVATE);