1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
22 #if !defined(PLATFORM_WIN32)
24 #include <sys/param.h>
34 #define NUM_LEVELCLASS_DESC 8
36 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
49 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
50 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
51 IS_LEVELCLASS_BD(n) ? FC_YELLOW : \
52 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
53 IS_LEVELCLASS_SP(n) ? FC_YELLOW : \
54 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
55 IS_LEVELCLASS_SB(n) ? FC_YELLOW : \
56 IS_LEVELCLASS_CONTRIB(n) ? FC_GREEN : \
57 IS_LEVELCLASS_PRIVATE(n) ? FC_RED : \
60 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
61 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
62 IS_LEVELCLASS_BD(n) ? 2 : \
63 IS_LEVELCLASS_EM(n) ? 3 : \
64 IS_LEVELCLASS_SP(n) ? 4 : \
65 IS_LEVELCLASS_DX(n) ? 5 : \
66 IS_LEVELCLASS_SB(n) ? 6 : \
67 IS_LEVELCLASS_CONTRIB(n) ? 7 : \
68 IS_LEVELCLASS_PRIVATE(n) ? 8 : \
71 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
72 IS_ARTWORKCLASS_CONTRIB(n) ? FC_GREEN : \
73 IS_ARTWORKCLASS_PRIVATE(n) ? FC_RED : \
74 IS_ARTWORKCLASS_LEVEL(n) ? FC_YELLOW : \
77 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
78 IS_ARTWORKCLASS_LEVEL(n) ? 1 : \
79 IS_ARTWORKCLASS_CONTRIB(n) ? 2 : \
80 IS_ARTWORKCLASS_PRIVATE(n) ? 3 : \
83 #define TOKEN_VALUE_POSITION_SHORT 32
84 #define TOKEN_VALUE_POSITION_DEFAULT 40
85 #define TOKEN_COMMENT_POSITION_DEFAULT 60
87 #define MAX_COOKIE_LEN 256
89 static int token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
90 static int token_comment_position = TOKEN_COMMENT_POSITION_DEFAULT;
93 /* ------------------------------------------------------------------------- */
95 /* ------------------------------------------------------------------------- */
97 static char *getLevelClassDescription(TreeInfo *ldi)
99 int position = ldi->sort_priority / 100;
101 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
102 return levelclass_desc[position];
104 return "Unknown Level Class";
107 static char *getUserLevelDir(char *level_subdir)
109 static char *userlevel_dir = NULL;
110 char *data_dir = getUserGameDataDir();
111 char *userlevel_subdir = LEVELS_DIRECTORY;
113 checked_free(userlevel_dir);
115 if (level_subdir != NULL)
116 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
118 userlevel_dir = getPath2(data_dir, userlevel_subdir);
120 return userlevel_dir;
123 static char *getScoreDir(char *level_subdir)
125 static char *score_dir = NULL;
126 char *data_dir = getCommonDataDir();
127 char *score_subdir = SCORES_DIRECTORY;
129 checked_free(score_dir);
131 if (level_subdir != NULL)
132 score_dir = getPath3(data_dir, score_subdir, level_subdir);
134 score_dir = getPath2(data_dir, score_subdir);
139 static char *getLevelSetupDir(char *level_subdir)
141 static char *levelsetup_dir = NULL;
142 char *data_dir = getUserGameDataDir();
143 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
145 checked_free(levelsetup_dir);
147 if (level_subdir != NULL)
148 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
150 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
152 return levelsetup_dir;
155 static char *getLevelDirFromTreeInfo(TreeInfo *node)
157 static char *level_dir = NULL;
160 return options.level_directory;
162 checked_free(level_dir);
164 level_dir = getPath2((node->in_user_dir ? getUserLevelDir(NULL) :
165 options.level_directory), node->fullpath);
170 char *getCurrentLevelDir()
172 return getLevelDirFromTreeInfo(leveldir_current);
175 static char *getTapeDir(char *level_subdir)
177 static char *tape_dir = NULL;
178 char *data_dir = getUserGameDataDir();
179 char *tape_subdir = TAPES_DIRECTORY;
181 checked_free(tape_dir);
183 if (level_subdir != NULL)
184 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
186 tape_dir = getPath2(data_dir, tape_subdir);
191 static char *getSolutionTapeDir()
193 static char *tape_dir = NULL;
194 char *data_dir = getCurrentLevelDir();
195 char *tape_subdir = TAPES_DIRECTORY;
197 checked_free(tape_dir);
199 tape_dir = getPath2(data_dir, tape_subdir);
204 static char *getDefaultGraphicsDir(char *graphics_subdir)
206 static char *graphics_dir = NULL;
208 if (graphics_subdir == NULL)
209 return options.graphics_directory;
211 checked_free(graphics_dir);
213 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
218 static char *getDefaultSoundsDir(char *sounds_subdir)
220 static char *sounds_dir = NULL;
222 if (sounds_subdir == NULL)
223 return options.sounds_directory;
225 checked_free(sounds_dir);
227 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
232 static char *getDefaultMusicDir(char *music_subdir)
234 static char *music_dir = NULL;
236 if (music_subdir == NULL)
237 return options.music_directory;
239 checked_free(music_dir);
241 music_dir = getPath2(options.music_directory, music_subdir);
246 static char *getDefaultArtworkSet(int type)
248 return (type == TREE_TYPE_GRAPHICS_DIR ? GFX_CLASSIC_SUBDIR :
249 type == TREE_TYPE_SOUNDS_DIR ? SND_CLASSIC_SUBDIR :
250 type == TREE_TYPE_MUSIC_DIR ? MUS_CLASSIC_SUBDIR : "");
253 static char *getDefaultArtworkDir(int type)
255 return (type == TREE_TYPE_GRAPHICS_DIR ?
256 getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR) :
257 type == TREE_TYPE_SOUNDS_DIR ?
258 getDefaultSoundsDir(SND_CLASSIC_SUBDIR) :
259 type == TREE_TYPE_MUSIC_DIR ?
260 getDefaultMusicDir(MUS_CLASSIC_SUBDIR) : "");
263 static char *getUserGraphicsDir()
265 static char *usergraphics_dir = NULL;
267 if (usergraphics_dir == NULL)
268 usergraphics_dir = getPath2(getUserGameDataDir(), GRAPHICS_DIRECTORY);
270 return usergraphics_dir;
273 static char *getUserSoundsDir()
275 static char *usersounds_dir = NULL;
277 if (usersounds_dir == NULL)
278 usersounds_dir = getPath2(getUserGameDataDir(), SOUNDS_DIRECTORY);
280 return usersounds_dir;
283 static char *getUserMusicDir()
285 static char *usermusic_dir = NULL;
287 if (usermusic_dir == NULL)
288 usermusic_dir = getPath2(getUserGameDataDir(), MUSIC_DIRECTORY);
290 return usermusic_dir;
293 static char *getSetupArtworkDir(TreeInfo *ti)
295 static char *artwork_dir = NULL;
297 checked_free(artwork_dir);
299 artwork_dir = getPath2(ti->basepath, ti->fullpath);
304 char *setLevelArtworkDir(TreeInfo *ti)
306 char **artwork_path_ptr, **artwork_set_ptr;
307 TreeInfo *level_artwork;
309 if (ti == NULL || leveldir_current == NULL)
312 artwork_path_ptr = LEVELDIR_ARTWORK_PATH_PTR(leveldir_current, ti->type);
313 artwork_set_ptr = LEVELDIR_ARTWORK_SET_PTR( leveldir_current, ti->type);
315 checked_free(*artwork_path_ptr);
317 if ((level_artwork = getTreeInfoFromIdentifier(ti, *artwork_set_ptr)))
318 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
321 /* No (or non-existing) artwork configured in "levelinfo.conf". This would
322 normally result in using the artwork configured in the setup menu. But
323 if an artwork subdirectory exists (which might contain custom artwork
324 or an artwork configuration file), this level artwork must be treated
325 as relative to the default "classic" artwork, not to the artwork that
326 is currently configured in the setup menu. */
328 char *dir = getPath2(getCurrentLevelDir(), ARTWORK_DIRECTORY(ti->type));
330 checked_free(*artwork_set_ptr);
334 *artwork_path_ptr = getStringCopy(getDefaultArtworkDir(ti->type));
335 *artwork_set_ptr = getStringCopy(getDefaultArtworkSet(ti->type));
339 *artwork_path_ptr = getStringCopy(UNDEFINED_FILENAME);
340 *artwork_set_ptr = NULL;
346 return *artwork_set_ptr;
349 inline static char *getLevelArtworkSet(int type)
351 if (leveldir_current == NULL)
354 return LEVELDIR_ARTWORK_SET(leveldir_current, type);
357 inline static char *getLevelArtworkDir(int type)
359 if (leveldir_current == NULL)
360 return UNDEFINED_FILENAME;
362 return LEVELDIR_ARTWORK_PATH(leveldir_current, type);
365 char *getTapeFilename(int nr)
367 static char *filename = NULL;
368 char basename[MAX_FILENAME_LEN];
370 checked_free(filename);
372 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
373 filename = getPath2(getTapeDir(leveldir_current->subdir), basename);
378 char *getSolutionTapeFilename(int nr)
380 static char *filename = NULL;
381 char basename[MAX_FILENAME_LEN];
383 checked_free(filename);
385 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
386 filename = getPath2(getSolutionTapeDir(), basename);
391 char *getScoreFilename(int nr)
393 static char *filename = NULL;
394 char basename[MAX_FILENAME_LEN];
396 checked_free(filename);
398 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
399 filename = getPath2(getScoreDir(leveldir_current->subdir), basename);
404 char *getSetupFilename()
406 static char *filename = NULL;
408 checked_free(filename);
410 filename = getPath2(getSetupDir(), SETUP_FILENAME);
415 char *getEditorSetupFilename()
417 static char *filename = NULL;
419 checked_free(filename);
420 filename = getPath2(getCurrentLevelDir(), EDITORSETUP_FILENAME);
422 if (fileExists(filename))
425 checked_free(filename);
426 filename = getPath2(getSetupDir(), EDITORSETUP_FILENAME);
431 char *getHelpAnimFilename()
433 static char *filename = NULL;
435 checked_free(filename);
437 filename = getPath2(getCurrentLevelDir(), HELPANIM_FILENAME);
442 char *getHelpTextFilename()
444 static char *filename = NULL;
446 checked_free(filename);
448 filename = getPath2(getCurrentLevelDir(), HELPTEXT_FILENAME);
453 char *getLevelSetInfoFilename()
455 static char *filename = NULL;
470 for (i = 0; basenames[i] != NULL; i++)
472 checked_free(filename);
473 filename = getPath2(getCurrentLevelDir(), basenames[i]);
475 if (fileExists(filename))
482 static char *getCorrectedArtworkBasename(char *basename)
484 char *basename_corrected = basename;
486 #if defined(PLATFORM_MSDOS)
487 if (program.filename_prefix != NULL)
489 int prefix_len = strlen(program.filename_prefix);
491 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
492 basename_corrected = &basename[prefix_len];
494 /* if corrected filename is still longer than standard MS-DOS filename
495 size (8 characters + 1 dot + 3 characters file extension), shorten
496 filename by writing file extension after 8th basename character */
497 if (strlen(basename_corrected) > 8 + 1 + 3)
499 static char *msdos_filename = NULL;
501 checked_free(msdos_filename);
503 msdos_filename = getStringCopy(basename_corrected);
504 strncpy(&msdos_filename[8], &basename[strlen(basename) - (1+3)], 1+3 +1);
506 basename_corrected = msdos_filename;
511 return basename_corrected;
514 char *getCustomImageFilename(char *basename)
516 static char *filename = NULL;
517 boolean skip_setup_artwork = FALSE;
519 checked_free(filename);
521 basename = getCorrectedArtworkBasename(basename);
523 if (!setup.override_level_graphics)
525 /* 1st try: look for special artwork in current level series directory */
526 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
527 if (fileExists(filename))
532 /* check if there is special artwork configured in level series config */
533 if (getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) != NULL)
535 /* 2nd try: look for special artwork configured in level series config */
536 filename = getPath2(getLevelArtworkDir(ARTWORK_TYPE_GRAPHICS), basename);
537 if (fileExists(filename))
542 /* take missing artwork configured in level set config from default */
543 skip_setup_artwork = TRUE;
547 if (!skip_setup_artwork)
549 /* 3rd try: look for special artwork in configured artwork directory */
550 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
551 if (fileExists(filename))
557 /* 4th try: look for default artwork in new default artwork directory */
558 filename = getPath2(getDefaultGraphicsDir(GFX_CLASSIC_SUBDIR), basename);
559 if (fileExists(filename))
564 /* 5th try: look for default artwork in old default artwork directory */
565 filename = getPath2(options.graphics_directory, basename);
566 if (fileExists(filename))
569 return NULL; /* cannot find specified artwork file anywhere */
572 char *getCustomSoundFilename(char *basename)
574 static char *filename = NULL;
575 boolean skip_setup_artwork = FALSE;
577 checked_free(filename);
579 basename = getCorrectedArtworkBasename(basename);
581 if (!setup.override_level_sounds)
583 /* 1st try: look for special artwork in current level series directory */
584 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
585 if (fileExists(filename))
590 /* check if there is special artwork configured in level series config */
591 if (getLevelArtworkSet(ARTWORK_TYPE_SOUNDS) != NULL)
593 /* 2nd try: look for special artwork configured in level series config */
594 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
595 if (fileExists(filename))
600 /* take missing artwork configured in level set config from default */
601 skip_setup_artwork = TRUE;
605 if (!skip_setup_artwork)
607 /* 3rd try: look for special artwork in configured artwork directory */
608 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
609 if (fileExists(filename))
615 /* 4th try: look for default artwork in new default artwork directory */
616 filename = getPath2(getDefaultSoundsDir(SND_CLASSIC_SUBDIR), basename);
617 if (fileExists(filename))
622 /* 5th try: look for default artwork in old default artwork directory */
623 filename = getPath2(options.sounds_directory, basename);
624 if (fileExists(filename))
627 return NULL; /* cannot find specified artwork file anywhere */
630 char *getCustomMusicFilename(char *basename)
632 static char *filename = NULL;
633 boolean skip_setup_artwork = FALSE;
635 checked_free(filename);
637 basename = getCorrectedArtworkBasename(basename);
639 if (!setup.override_level_music)
641 /* 1st try: look for special artwork in current level series directory */
642 filename = getPath3(getCurrentLevelDir(), MUSIC_DIRECTORY, basename);
643 if (fileExists(filename))
648 /* check if there is special artwork configured in level series config */
649 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
651 /* 2nd try: look for special artwork configured in level series config */
652 filename = getPath2(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR), basename);
653 if (fileExists(filename))
658 /* take missing artwork configured in level set config from default */
659 skip_setup_artwork = TRUE;
663 if (!skip_setup_artwork)
665 /* 3rd try: look for special artwork in configured artwork directory */
666 filename = getPath2(getSetupArtworkDir(artwork.mus_current), basename);
667 if (fileExists(filename))
673 /* 4th try: look for default artwork in new default artwork directory */
674 filename = getPath2(getDefaultMusicDir(MUS_CLASSIC_SUBDIR), basename);
675 if (fileExists(filename))
680 /* 5th try: look for default artwork in old default artwork directory */
681 filename = getPath2(options.music_directory, basename);
682 if (fileExists(filename))
685 return NULL; /* cannot find specified artwork file anywhere */
688 char *getCustomArtworkFilename(char *basename, int type)
690 if (type == ARTWORK_TYPE_GRAPHICS)
691 return getCustomImageFilename(basename);
692 else if (type == ARTWORK_TYPE_SOUNDS)
693 return getCustomSoundFilename(basename);
694 else if (type == ARTWORK_TYPE_MUSIC)
695 return getCustomMusicFilename(basename);
697 return UNDEFINED_FILENAME;
700 char *getCustomArtworkConfigFilename(int type)
702 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
705 char *getCustomArtworkLevelConfigFilename(int type)
707 static char *filename = NULL;
709 checked_free(filename);
711 filename = getPath2(getLevelArtworkDir(type), ARTWORKINFO_FILENAME(type));
716 char *getCustomMusicDirectory(void)
718 static char *directory = NULL;
719 boolean skip_setup_artwork = FALSE;
721 checked_free(directory);
723 if (!setup.override_level_music)
725 /* 1st try: look for special artwork in current level series directory */
726 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
727 if (fileExists(directory))
732 /* check if there is special artwork configured in level series config */
733 if (getLevelArtworkSet(ARTWORK_TYPE_MUSIC) != NULL)
735 /* 2nd try: look for special artwork configured in level series config */
736 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
737 if (fileExists(directory))
742 /* take missing artwork configured in level set config from default */
743 skip_setup_artwork = TRUE;
747 if (!skip_setup_artwork)
749 /* 3rd try: look for special artwork in configured artwork directory */
750 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
751 if (fileExists(directory))
757 /* 4th try: look for default artwork in new default artwork directory */
758 directory = getStringCopy(getDefaultMusicDir(MUS_CLASSIC_SUBDIR));
759 if (fileExists(directory))
764 /* 5th try: look for default artwork in old default artwork directory */
765 directory = getStringCopy(options.music_directory);
766 if (fileExists(directory))
769 return NULL; /* cannot find specified artwork file anywhere */
772 void InitTapeDirectory(char *level_subdir)
774 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
775 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
776 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
779 void InitScoreDirectory(char *level_subdir)
781 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
782 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
783 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
786 static void SaveUserLevelInfo();
788 void InitUserLevelDirectory(char *level_subdir)
790 if (!fileExists(getUserLevelDir(level_subdir)))
792 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
793 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
794 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
800 void InitLevelSetupDirectory(char *level_subdir)
802 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
803 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
804 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
808 /* ------------------------------------------------------------------------- */
809 /* some functions to handle lists of level and artwork directories */
810 /* ------------------------------------------------------------------------- */
812 TreeInfo *newTreeInfo()
814 return checked_calloc(sizeof(TreeInfo));
817 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
819 node_new->next = *node_first;
820 *node_first = node_new;
823 int numTreeInfo(TreeInfo *node)
836 boolean validLevelSeries(TreeInfo *node)
838 return (node != NULL && !node->node_group && !node->parent_link);
841 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
846 if (node->node_group) /* enter level group (step down into tree) */
847 return getFirstValidTreeInfoEntry(node->node_group);
848 else if (node->parent_link) /* skip start entry of level group */
850 if (node->next) /* get first real level series entry */
851 return getFirstValidTreeInfoEntry(node->next);
852 else /* leave empty level group and go on */
853 return getFirstValidTreeInfoEntry(node->node_parent->next);
855 else /* this seems to be a regular level series */
859 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
864 if (node->node_parent == NULL) /* top level group */
865 return *node->node_top;
866 else /* sub level group */
867 return node->node_parent->node_group;
870 int numTreeInfoInGroup(TreeInfo *node)
872 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
875 int posTreeInfo(TreeInfo *node)
877 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
882 if (node_cmp == node)
886 node_cmp = node_cmp->next;
892 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
894 TreeInfo *node_default = node;
909 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
911 if (identifier == NULL)
916 if (node->node_group)
918 TreeInfo *node_group;
920 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
925 else if (!node->parent_link)
927 if (strEqual(identifier, node->identifier))
937 TreeInfo *cloneTreeNode(TreeInfo **node_top, TreeInfo *node_parent,
938 TreeInfo *node, boolean skip_sets_without_levels)
945 if (!node->parent_link && !node->level_group &&
946 skip_sets_without_levels && node->levels == 0)
947 return cloneTreeNode(node_top, node_parent, node->next,
948 skip_sets_without_levels);
950 node_new = newTreeInfo();
952 *node_new = *node; /* copy complete node */
954 node_new->node_top = node_top; /* correct top node link */
955 node_new->node_parent = node_parent; /* correct parent node link */
957 if (node->level_group)
958 node_new->node_group = cloneTreeNode(node_top, node_new, node->node_group,
959 skip_sets_without_levels);
961 node_new->next = cloneTreeNode(node_top, node_parent, node->next,
962 skip_sets_without_levels);
967 void cloneTree(TreeInfo **ti_new, TreeInfo *ti, boolean skip_empty_sets)
969 TreeInfo *ti_cloned = cloneTreeNode(ti_new, NULL, ti, skip_empty_sets);
974 static boolean adjustTreeGraphicsForEMC(TreeInfo *node)
976 boolean settings_changed = FALSE;
980 if (node->graphics_set_ecs && !setup.prefer_aga_graphics &&
981 !strEqual(node->graphics_set, node->graphics_set_ecs))
983 setString(&node->graphics_set, node->graphics_set_ecs);
984 settings_changed = TRUE;
986 else if (node->graphics_set_aga && setup.prefer_aga_graphics &&
987 !strEqual(node->graphics_set, node->graphics_set_aga))
989 setString(&node->graphics_set, node->graphics_set_aga);
990 settings_changed = TRUE;
993 if (node->node_group != NULL)
994 settings_changed |= adjustTreeGraphicsForEMC(node->node_group);
999 return settings_changed;
1002 void dumpTreeInfo(TreeInfo *node, int depth)
1006 printf("Dumping TreeInfo:\n");
1010 for (i = 0; i < (depth + 1) * 3; i++)
1013 printf("subdir == '%s' ['%s', '%s'] [%d])\n",
1014 node->subdir, node->fullpath, node->basepath, node->in_user_dir);
1016 if (node->node_group != NULL)
1017 dumpTreeInfo(node->node_group, depth + 1);
1023 void sortTreeInfo(TreeInfo **node_first,
1024 int (*compare_function)(const void *, const void *))
1026 int num_nodes = numTreeInfo(*node_first);
1027 TreeInfo **sort_array;
1028 TreeInfo *node = *node_first;
1034 /* allocate array for sorting structure pointers */
1035 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
1037 /* writing structure pointers to sorting array */
1038 while (i < num_nodes && node) /* double boundary check... */
1040 sort_array[i] = node;
1046 /* sorting the structure pointers in the sorting array */
1047 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
1050 /* update the linkage of list elements with the sorted node array */
1051 for (i = 0; i < num_nodes - 1; i++)
1052 sort_array[i]->next = sort_array[i + 1];
1053 sort_array[num_nodes - 1]->next = NULL;
1055 /* update the linkage of the main list anchor pointer */
1056 *node_first = sort_array[0];
1060 /* now recursively sort the level group structures */
1064 if (node->node_group != NULL)
1065 sortTreeInfo(&node->node_group, compare_function);
1072 /* ========================================================================= */
1073 /* some stuff from "files.c" */
1074 /* ========================================================================= */
1076 #if defined(PLATFORM_WIN32)
1078 #define S_IRGRP S_IRUSR
1081 #define S_IROTH S_IRUSR
1084 #define S_IWGRP S_IWUSR
1087 #define S_IWOTH S_IWUSR
1090 #define S_IXGRP S_IXUSR
1093 #define S_IXOTH S_IXUSR
1096 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
1101 #endif /* PLATFORM_WIN32 */
1103 /* file permissions for newly written files */
1104 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
1105 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
1106 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
1108 #define MODE_W_PRIVATE (S_IWUSR)
1109 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
1110 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
1112 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
1113 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
1115 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
1116 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
1120 static char *dir = NULL;
1122 #if defined(PLATFORM_WIN32)
1125 dir = checked_malloc(MAX_PATH + 1);
1127 if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, dir)))
1130 #elif defined(PLATFORM_UNIX)
1133 if ((dir = getenv("HOME")) == NULL)
1137 if ((pwd = getpwuid(getuid())) != NULL)
1138 dir = getStringCopy(pwd->pw_dir);
1150 char *getCommonDataDir(void)
1152 static char *common_data_dir = NULL;
1154 #if defined(PLATFORM_WIN32)
1155 if (common_data_dir == NULL)
1157 char *dir = checked_malloc(MAX_PATH + 1);
1159 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
1160 && !strEqual(dir, "")) /* empty for Windows 95/98 */
1161 common_data_dir = getPath2(dir, program.userdata_directory);
1163 common_data_dir = options.rw_base_directory;
1166 if (common_data_dir == NULL)
1167 common_data_dir = options.rw_base_directory;
1170 return common_data_dir;
1173 char *getPersonalDataDir(void)
1175 static char *personal_data_dir = NULL;
1177 #if defined(PLATFORM_MACOSX)
1178 if (personal_data_dir == NULL)
1179 personal_data_dir = getPath2(getHomeDir(), "Documents");
1181 if (personal_data_dir == NULL)
1182 personal_data_dir = getHomeDir();
1185 return personal_data_dir;
1188 char *getUserGameDataDir(void)
1190 if (program.userdata_path == NULL)
1191 program.userdata_path = getPath2(getPersonalDataDir(),
1192 program.userdata_subdir);
1194 return program.userdata_path;
1197 void fixUserGameDataDir()
1199 #if defined(PLATFORM_MACOSX)
1200 char *userdata_dir_old = getPath2(getHomeDir(), program.userdata_subdir_unix);
1201 char *userdata_dir_new = getUserGameDataDir();
1203 /* convert old Unix style game data directory to Mac OS X style, if needed */
1204 if (fileExists(userdata_dir_old) && !fileExists(userdata_dir_new))
1206 if (rename(userdata_dir_old, userdata_dir_new) != 0)
1208 Error(ERR_WARN, "cannot move game data directory '%s' to '%s'",
1209 userdata_dir_old, userdata_dir_new);
1211 /* continue using Unix style data directory -- this should not happen */
1212 program.userdata_path = getPath2(getPersonalDataDir(),
1213 program.userdata_subdir_unix);
1217 free(userdata_dir_old);
1218 free(userdata_dir_new);
1224 return getUserGameDataDir();
1227 static mode_t posix_umask(mode_t mask)
1229 #if defined(PLATFORM_UNIX)
1236 static int posix_mkdir(const char *pathname, mode_t mode)
1238 #if defined(PLATFORM_WIN32)
1239 return mkdir(pathname);
1241 return mkdir(pathname, mode);
1245 void createDirectory(char *dir, char *text, int permission_class)
1247 /* leave "other" permissions in umask untouched, but ensure group parts
1248 of USERDATA_DIR_MODE are not masked */
1249 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
1250 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
1251 mode_t normal_umask = posix_umask(0);
1252 mode_t group_umask = ~(dir_mode & S_IRWXG);
1253 posix_umask(normal_umask & group_umask);
1255 if (!fileExists(dir))
1256 if (posix_mkdir(dir, dir_mode) != 0)
1257 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
1259 posix_umask(normal_umask); /* reset normal umask */
1262 void InitUserDataDirectory()
1264 createDirectory(getUserGameDataDir(), "user data", PERMS_PRIVATE);
1267 void SetFilePermissions(char *filename, int permission_class)
1269 chmod(filename, (permission_class == PERMS_PRIVATE ?
1270 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
1273 char *getCookie(char *file_type)
1275 static char cookie[MAX_COOKIE_LEN + 1];
1277 if (strlen(program.cookie_prefix) + 1 +
1278 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
1279 return "[COOKIE ERROR]"; /* should never happen */
1281 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
1282 program.cookie_prefix, file_type,
1283 program.version_major, program.version_minor);
1288 int getFileVersionFromCookieString(const char *cookie)
1290 const char *ptr_cookie1, *ptr_cookie2;
1291 const char *pattern1 = "_FILE_VERSION_";
1292 const char *pattern2 = "?.?";
1293 const int len_cookie = strlen(cookie);
1294 const int len_pattern1 = strlen(pattern1);
1295 const int len_pattern2 = strlen(pattern2);
1296 const int len_pattern = len_pattern1 + len_pattern2;
1297 int version_major, version_minor;
1299 if (len_cookie <= len_pattern)
1302 ptr_cookie1 = &cookie[len_cookie - len_pattern];
1303 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1305 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1308 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1309 ptr_cookie2[1] != '.' ||
1310 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1313 version_major = ptr_cookie2[0] - '0';
1314 version_minor = ptr_cookie2[2] - '0';
1316 return VERSION_IDENT(version_major, version_minor, 0, 0);
1319 boolean checkCookieString(const char *cookie, const char *template)
1321 const char *pattern = "_FILE_VERSION_?.?";
1322 const int len_cookie = strlen(cookie);
1323 const int len_template = strlen(template);
1324 const int len_pattern = strlen(pattern);
1326 if (len_cookie != len_template)
1329 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1335 /* ------------------------------------------------------------------------- */
1336 /* setup file list and hash handling functions */
1337 /* ------------------------------------------------------------------------- */
1339 char *getFormattedSetupEntry(char *token, char *value)
1342 static char entry[MAX_LINE_LEN];
1344 /* if value is an empty string, just return token without value */
1348 /* start with the token and some spaces to format output line */
1349 sprintf(entry, "%s:", token);
1350 for (i = strlen(entry); i < token_value_position; i++)
1353 /* continue with the token's value */
1354 strcat(entry, value);
1359 SetupFileList *newSetupFileList(char *token, char *value)
1361 SetupFileList *new = checked_malloc(sizeof(SetupFileList));
1363 new->token = getStringCopy(token);
1364 new->value = getStringCopy(value);
1371 void freeSetupFileList(SetupFileList *list)
1376 checked_free(list->token);
1377 checked_free(list->value);
1380 freeSetupFileList(list->next);
1385 char *getListEntry(SetupFileList *list, char *token)
1390 if (strEqual(list->token, token))
1393 return getListEntry(list->next, token);
1396 SetupFileList *setListEntry(SetupFileList *list, char *token, char *value)
1401 if (strEqual(list->token, token))
1403 checked_free(list->value);
1405 list->value = getStringCopy(value);
1409 else if (list->next == NULL)
1410 return (list->next = newSetupFileList(token, value));
1412 return setListEntry(list->next, token, value);
1415 SetupFileList *addListEntry(SetupFileList *list, char *token, char *value)
1420 if (list->next == NULL)
1421 return (list->next = newSetupFileList(token, value));
1423 return addListEntry(list->next, token, value);
1427 static void printSetupFileList(SetupFileList *list)
1432 printf("token: '%s'\n", list->token);
1433 printf("value: '%s'\n", list->value);
1435 printSetupFileList(list->next);
1440 DEFINE_HASHTABLE_INSERT(insert_hash_entry, char, char);
1441 DEFINE_HASHTABLE_SEARCH(search_hash_entry, char, char);
1442 DEFINE_HASHTABLE_CHANGE(change_hash_entry, char, char);
1443 DEFINE_HASHTABLE_REMOVE(remove_hash_entry, char, char);
1445 #define insert_hash_entry hashtable_insert
1446 #define search_hash_entry hashtable_search
1447 #define change_hash_entry hashtable_change
1448 #define remove_hash_entry hashtable_remove
1451 static unsigned int get_hash_from_key(void *key)
1456 This algorithm (k=33) was first reported by Dan Bernstein many years ago in
1457 'comp.lang.c'. Another version of this algorithm (now favored by Bernstein)
1458 uses XOR: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33 (why
1459 it works better than many other constants, prime or not) has never been
1460 adequately explained.
1462 If you just want to have a good hash function, and cannot wait, djb2
1463 is one of the best string hash functions i know. It has excellent
1464 distribution and speed on many different sets of keys and table sizes.
1465 You are not likely to do better with one of the "well known" functions
1466 such as PJW, K&R, etc.
1468 Ozan (oz) Yigit [http://www.cs.yorku.ca/~oz/hash.html]
1471 char *str = (char *)key;
1472 unsigned int hash = 5381;
1475 while ((c = *str++))
1476 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
1481 static int keys_are_equal(void *key1, void *key2)
1483 return (strEqual((char *)key1, (char *)key2));
1486 SetupFileHash *newSetupFileHash()
1488 SetupFileHash *new_hash =
1489 create_hashtable(16, 0.75, get_hash_from_key, keys_are_equal);
1491 if (new_hash == NULL)
1492 Error(ERR_EXIT, "create_hashtable() failed -- out of memory");
1497 void freeSetupFileHash(SetupFileHash *hash)
1502 hashtable_destroy(hash, 1); /* 1 == also free values stored in hash */
1505 char *getHashEntry(SetupFileHash *hash, char *token)
1510 return search_hash_entry(hash, token);
1513 void setHashEntry(SetupFileHash *hash, char *token, char *value)
1520 value_copy = getStringCopy(value);
1522 /* change value; if it does not exist, insert it as new */
1523 if (!change_hash_entry(hash, token, value_copy))
1524 if (!insert_hash_entry(hash, getStringCopy(token), value_copy))
1525 Error(ERR_EXIT, "cannot insert into hash -- aborting");
1528 char *removeHashEntry(SetupFileHash *hash, char *token)
1533 return remove_hash_entry(hash, token);
1537 static void printSetupFileHash(SetupFileHash *hash)
1539 BEGIN_HASH_ITERATION(hash, itr)
1541 printf("token: '%s'\n", HASH_ITERATION_TOKEN(itr));
1542 printf("value: '%s'\n", HASH_ITERATION_VALUE(itr));
1544 END_HASH_ITERATION(hash, itr)
1548 static void *loadSetupFileData(char *filename, boolean use_hash)
1550 char line[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
1551 char *token, *value, *line_ptr;
1552 void *setup_file_data, *insert_ptr = NULL;
1553 boolean read_continued_line = FALSE;
1556 if (!(file = fopen(filename, MODE_READ)))
1558 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1564 setup_file_data = newSetupFileHash();
1566 insert_ptr = setup_file_data = newSetupFileList("", "");
1570 /* read next line of input file */
1571 if (!fgets(line, MAX_LINE_LEN, file))
1574 /* cut trailing newline or carriage return */
1575 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1576 if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
1579 if (read_continued_line)
1581 /* cut leading whitespaces from input line */
1582 for (line_ptr = line; *line_ptr; line_ptr++)
1583 if (*line_ptr != ' ' && *line_ptr != '\t')
1586 /* append new line to existing line, if there is enough space */
1587 if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
1588 strcat(previous_line, line_ptr);
1590 strcpy(line, previous_line); /* copy storage buffer to line */
1592 read_continued_line = FALSE;
1595 /* if the last character is '\', continue at next line */
1596 if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
1598 line[strlen(line) - 1] = '\0'; /* cut off trailing backslash */
1599 strcpy(previous_line, line); /* copy line to storage buffer */
1601 read_continued_line = TRUE;
1606 /* cut trailing comment from input line */
1607 for (line_ptr = line; *line_ptr; line_ptr++)
1609 if (*line_ptr == '#')
1616 /* cut trailing whitespaces from input line */
1617 for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
1618 if ((*line_ptr == ' ' || *line_ptr == '\t') && *(line_ptr + 1) == '\0')
1621 /* ignore empty lines */
1625 /* cut leading whitespaces from token */
1626 for (token = line; *token; token++)
1627 if (*token != ' ' && *token != '\t')
1630 /* start with empty value as reliable default */
1633 /* find end of token to determine start of value */
1634 for (line_ptr = token; *line_ptr; line_ptr++)
1636 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1638 *line_ptr = '\0'; /* terminate token string */
1639 value = line_ptr + 1; /* set beginning of value */
1645 /* cut leading whitespaces from value */
1646 for (; *value; value++)
1647 if (*value != ' ' && *value != '\t')
1652 value = "true"; /* treat tokens without value as "true" */
1658 setHashEntry((SetupFileHash *)setup_file_data, token, value);
1660 insert_ptr = addListEntry((SetupFileList *)insert_ptr, token, value);
1668 if (hashtable_count((SetupFileHash *)setup_file_data) == 0)
1669 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1673 SetupFileList *setup_file_list = (SetupFileList *)setup_file_data;
1674 SetupFileList *first_valid_list_entry = setup_file_list->next;
1676 /* free empty list header */
1677 setup_file_list->next = NULL;
1678 freeSetupFileList(setup_file_list);
1679 setup_file_data = first_valid_list_entry;
1681 if (first_valid_list_entry == NULL)
1682 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1685 return setup_file_data;
1688 SetupFileList *loadSetupFileList(char *filename)
1690 return (SetupFileList *)loadSetupFileData(filename, FALSE);
1693 SetupFileHash *loadSetupFileHash(char *filename)
1695 return (SetupFileHash *)loadSetupFileData(filename, TRUE);
1698 void checkSetupFileHashIdentifier(SetupFileHash *setup_file_hash,
1699 char *filename, char *identifier)
1701 char *value = getHashEntry(setup_file_hash, TOKEN_STR_FILE_IDENTIFIER);
1704 Error(ERR_WARN, "config file '%s' has no file identifier", filename);
1705 else if (!checkCookieString(value, identifier))
1706 Error(ERR_WARN, "config file '%s' has wrong file identifier", filename);
1710 /* ========================================================================= */
1711 /* setup file stuff */
1712 /* ========================================================================= */
1714 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1715 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1716 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1718 /* level directory info */
1719 #define LEVELINFO_TOKEN_IDENTIFIER 0
1720 #define LEVELINFO_TOKEN_NAME 1
1721 #define LEVELINFO_TOKEN_NAME_SORTING 2
1722 #define LEVELINFO_TOKEN_AUTHOR 3
1723 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1724 #define LEVELINFO_TOKEN_IMPORTED_BY 5
1725 #define LEVELINFO_TOKEN_LEVELS 6
1726 #define LEVELINFO_TOKEN_FIRST_LEVEL 7
1727 #define LEVELINFO_TOKEN_SORT_PRIORITY 8
1728 #define LEVELINFO_TOKEN_LATEST_ENGINE 9
1729 #define LEVELINFO_TOKEN_LEVEL_GROUP 10
1730 #define LEVELINFO_TOKEN_READONLY 11
1731 #define LEVELINFO_TOKEN_GRAPHICS_SET_ECS 12
1732 #define LEVELINFO_TOKEN_GRAPHICS_SET_AGA 13
1733 #define LEVELINFO_TOKEN_GRAPHICS_SET 14
1734 #define LEVELINFO_TOKEN_SOUNDS_SET 15
1735 #define LEVELINFO_TOKEN_MUSIC_SET 16
1736 #define LEVELINFO_TOKEN_FILENAME 17
1737 #define LEVELINFO_TOKEN_FILETYPE 18
1738 #define LEVELINFO_TOKEN_HANDICAP 19
1739 #define LEVELINFO_TOKEN_SKIP_LEVELS 20
1741 #define NUM_LEVELINFO_TOKENS 21
1743 static LevelDirTree ldi;
1745 static struct TokenInfo levelinfo_tokens[] =
1747 /* level directory info */
1748 { TYPE_STRING, &ldi.identifier, "identifier" },
1749 { TYPE_STRING, &ldi.name, "name" },
1750 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1751 { TYPE_STRING, &ldi.author, "author" },
1752 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1753 { TYPE_STRING, &ldi.imported_by, "imported_by" },
1754 { TYPE_INTEGER, &ldi.levels, "levels" },
1755 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1756 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1757 { TYPE_BOOLEAN, &ldi.latest_engine, "latest_engine" },
1758 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1759 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1760 { TYPE_STRING, &ldi.graphics_set_ecs, "graphics_set.ecs" },
1761 { TYPE_STRING, &ldi.graphics_set_aga, "graphics_set.aga" },
1762 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1763 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1764 { TYPE_STRING, &ldi.music_set, "music_set" },
1765 { TYPE_STRING, &ldi.level_filename, "filename" },
1766 { TYPE_STRING, &ldi.level_filetype, "filetype" },
1767 { TYPE_BOOLEAN, &ldi.handicap, "handicap" },
1768 { TYPE_BOOLEAN, &ldi.skip_levels, "skip_levels" }
1771 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1775 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1776 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1777 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1778 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1781 ldi->node_parent = NULL;
1782 ldi->node_group = NULL;
1786 ldi->cl_cursor = -1;
1789 ldi->fullpath = NULL;
1790 ldi->basepath = NULL;
1791 ldi->identifier = NULL;
1792 ldi->name = getStringCopy(ANONYMOUS_NAME);
1793 ldi->name_sorting = NULL;
1794 ldi->author = getStringCopy(ANONYMOUS_NAME);
1796 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1797 ldi->latest_engine = FALSE; /* default: get from level */
1798 ldi->parent_link = FALSE;
1799 ldi->in_user_dir = FALSE;
1800 ldi->user_defined = FALSE;
1802 ldi->class_desc = NULL;
1804 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1806 ldi->imported_from = NULL;
1807 ldi->imported_by = NULL;
1809 ldi->graphics_set_ecs = NULL;
1810 ldi->graphics_set_aga = NULL;
1811 ldi->graphics_set = NULL;
1812 ldi->sounds_set = NULL;
1813 ldi->music_set = NULL;
1814 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1815 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1816 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1818 ldi->level_filename = NULL;
1819 ldi->level_filetype = NULL;
1822 ldi->first_level = 0;
1823 ldi->last_level = 0;
1824 ldi->level_group = FALSE;
1825 ldi->handicap_level = 0;
1826 ldi->readonly = TRUE;
1827 ldi->handicap = TRUE;
1828 ldi->skip_levels = FALSE;
1832 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1836 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1838 setTreeInfoToDefaults(ldi, TREE_TYPE_UNDEFINED);
1843 /* copy all values from the parent structure */
1845 ldi->type = parent->type;
1847 ldi->node_top = parent->node_top;
1848 ldi->node_parent = parent;
1849 ldi->node_group = NULL;
1853 ldi->cl_cursor = -1;
1856 ldi->fullpath = NULL;
1857 ldi->basepath = NULL;
1858 ldi->identifier = NULL;
1859 ldi->name = getStringCopy(ANONYMOUS_NAME);
1860 ldi->name_sorting = NULL;
1861 ldi->author = getStringCopy(parent->author);
1863 ldi->sort_priority = parent->sort_priority;
1864 ldi->latest_engine = parent->latest_engine;
1865 ldi->parent_link = FALSE;
1866 ldi->in_user_dir = parent->in_user_dir;
1867 ldi->user_defined = parent->user_defined;
1868 ldi->color = parent->color;
1869 ldi->class_desc = getStringCopy(parent->class_desc);
1871 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1873 ldi->imported_from = getStringCopy(parent->imported_from);
1874 ldi->imported_by = getStringCopy(parent->imported_by);
1876 ldi->graphics_set_ecs = NULL;
1877 ldi->graphics_set_aga = NULL;
1878 ldi->graphics_set = NULL;
1879 ldi->sounds_set = NULL;
1880 ldi->music_set = NULL;
1881 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1882 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1883 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1885 ldi->level_filename = NULL;
1886 ldi->level_filetype = NULL;
1889 ldi->first_level = 0;
1890 ldi->last_level = 0;
1891 ldi->level_group = FALSE;
1892 ldi->handicap_level = 0;
1893 ldi->readonly = TRUE;
1894 ldi->handicap = TRUE;
1895 ldi->skip_levels = FALSE;
1899 static void freeTreeInfo(TreeInfo *ldi)
1901 checked_free(ldi->subdir);
1902 checked_free(ldi->fullpath);
1903 checked_free(ldi->basepath);
1904 checked_free(ldi->identifier);
1906 checked_free(ldi->name);
1907 checked_free(ldi->name_sorting);
1908 checked_free(ldi->author);
1910 checked_free(ldi->class_desc);
1912 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1914 checked_free(ldi->imported_from);
1915 checked_free(ldi->imported_by);
1917 checked_free(ldi->graphics_set_ecs);
1918 checked_free(ldi->graphics_set_aga);
1919 checked_free(ldi->graphics_set);
1920 checked_free(ldi->sounds_set);
1921 checked_free(ldi->music_set);
1923 checked_free(ldi->graphics_path);
1924 checked_free(ldi->sounds_path);
1925 checked_free(ldi->music_path);
1927 checked_free(ldi->level_filename);
1928 checked_free(ldi->level_filetype);
1932 void setSetupInfo(struct TokenInfo *token_info,
1933 int token_nr, char *token_value)
1935 int token_type = token_info[token_nr].type;
1936 void *setup_value = token_info[token_nr].value;
1938 if (token_value == NULL)
1941 /* set setup field to corresponding token value */
1946 *(boolean *)setup_value = get_boolean_from_string(token_value);
1950 *(Key *)setup_value = getKeyFromKeyName(token_value);
1954 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1958 *(int *)setup_value = get_integer_from_string(token_value);
1962 checked_free(*(char **)setup_value);
1963 *(char **)setup_value = getStringCopy(token_value);
1971 static int compareTreeInfoEntries(const void *object1, const void *object2)
1973 const TreeInfo *entry1 = *((TreeInfo **)object1);
1974 const TreeInfo *entry2 = *((TreeInfo **)object2);
1975 int class_sorting1, class_sorting2;
1978 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1980 class_sorting1 = LEVELSORTING(entry1);
1981 class_sorting2 = LEVELSORTING(entry2);
1985 class_sorting1 = ARTWORKSORTING(entry1);
1986 class_sorting2 = ARTWORKSORTING(entry2);
1989 if (entry1->parent_link || entry2->parent_link)
1990 compare_result = (entry1->parent_link ? -1 : +1);
1991 else if (entry1->sort_priority == entry2->sort_priority)
1993 char *name1 = getStringToLower(entry1->name_sorting);
1994 char *name2 = getStringToLower(entry2->name_sorting);
1996 compare_result = strcmp(name1, name2);
2001 else if (class_sorting1 == class_sorting2)
2002 compare_result = entry1->sort_priority - entry2->sort_priority;
2004 compare_result = class_sorting1 - class_sorting2;
2006 return compare_result;
2009 static void createParentTreeInfoNode(TreeInfo *node_parent)
2013 if (node_parent == NULL)
2016 ti_new = newTreeInfo();
2017 setTreeInfoToDefaults(ti_new, node_parent->type);
2019 ti_new->node_parent = node_parent;
2020 ti_new->parent_link = TRUE;
2022 setString(&ti_new->identifier, node_parent->identifier);
2023 setString(&ti_new->name, ".. (parent directory)");
2024 setString(&ti_new->name_sorting, ti_new->name);
2026 setString(&ti_new->subdir, "..");
2027 setString(&ti_new->fullpath, node_parent->fullpath);
2029 ti_new->sort_priority = node_parent->sort_priority;
2030 ti_new->latest_engine = node_parent->latest_engine;
2032 setString(&ti_new->class_desc, getLevelClassDescription(ti_new));
2034 pushTreeInfo(&node_parent->node_group, ti_new);
2037 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
2038 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
2040 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
2041 TreeInfo *node_parent,
2042 char *level_directory,
2043 char *directory_name)
2045 char *directory_path = getPath2(level_directory, directory_name);
2046 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
2047 SetupFileHash *setup_file_hash;
2048 LevelDirTree *leveldir_new = NULL;
2051 /* unless debugging, silently ignore directories without "levelinfo.conf" */
2052 if (!options.debug && !fileExists(filename))
2054 free(directory_path);
2060 setup_file_hash = loadSetupFileHash(filename);
2062 if (setup_file_hash == NULL)
2064 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
2066 free(directory_path);
2072 leveldir_new = newTreeInfo();
2075 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
2077 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
2079 leveldir_new->subdir = getStringCopy(directory_name);
2081 checkSetupFileHashIdentifier(setup_file_hash, filename,
2082 getCookie("LEVELINFO"));
2084 /* set all structure fields according to the token/value pairs */
2085 ldi = *leveldir_new;
2086 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2087 setSetupInfo(levelinfo_tokens, i,
2088 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2089 *leveldir_new = ldi;
2091 if (strEqual(leveldir_new->name, ANONYMOUS_NAME))
2092 setString(&leveldir_new->name, leveldir_new->subdir);
2094 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
2096 if (leveldir_new->identifier == NULL)
2097 leveldir_new->identifier = getStringCopy(leveldir_new->subdir);
2099 if (leveldir_new->name_sorting == NULL)
2100 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
2102 if (node_parent == NULL) /* top level group */
2104 leveldir_new->basepath = getStringCopy(level_directory);
2105 leveldir_new->fullpath = getStringCopy(leveldir_new->subdir);
2107 else /* sub level group */
2109 leveldir_new->basepath = getStringCopy(node_parent->basepath);
2110 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2114 if (leveldir_new->levels < 1)
2115 leveldir_new->levels = 1;
2118 leveldir_new->last_level =
2119 leveldir_new->first_level + leveldir_new->levels - 1;
2121 leveldir_new->in_user_dir =
2122 (!strEqual(leveldir_new->basepath, options.level_directory));
2124 /* adjust some settings if user's private level directory was detected */
2125 if (leveldir_new->sort_priority == LEVELCLASS_UNDEFINED &&
2126 leveldir_new->in_user_dir &&
2127 (strEqual(leveldir_new->subdir, getLoginName()) ||
2128 strEqual(leveldir_new->name, getLoginName()) ||
2129 strEqual(leveldir_new->author, getRealName())))
2131 leveldir_new->sort_priority = LEVELCLASS_PRIVATE_START;
2132 leveldir_new->readonly = FALSE;
2135 leveldir_new->user_defined =
2136 (leveldir_new->in_user_dir && IS_LEVELCLASS_PRIVATE(leveldir_new));
2138 leveldir_new->color = LEVELCOLOR(leveldir_new);
2140 setString(&leveldir_new->class_desc, getLevelClassDescription(leveldir_new));
2142 leveldir_new->handicap_level = /* set handicap to default value */
2143 (leveldir_new->user_defined || !leveldir_new->handicap ?
2144 leveldir_new->last_level : leveldir_new->first_level);
2147 /* !!! don't skip sets without levels (else artwork base sets are missing) */
2149 if (leveldir_new->levels < 1 && !leveldir_new->level_group)
2151 /* skip level sets without levels (which are probably artwork base sets) */
2153 freeSetupFileHash(setup_file_hash);
2154 free(directory_path);
2162 pushTreeInfo(node_first, leveldir_new);
2164 freeSetupFileHash(setup_file_hash);
2166 if (leveldir_new->level_group)
2168 /* create node to link back to current level directory */
2169 createParentTreeInfoNode(leveldir_new);
2171 /* step into sub-directory and look for more level series */
2172 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
2173 leveldir_new, directory_path);
2176 free(directory_path);
2182 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
2183 TreeInfo *node_parent,
2184 char *level_directory)
2187 struct dirent *dir_entry;
2188 boolean valid_entry_found = FALSE;
2190 if ((dir = opendir(level_directory)) == NULL)
2192 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2196 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2198 struct stat file_status;
2199 char *directory_name = dir_entry->d_name;
2200 char *directory_path = getPath2(level_directory, directory_name);
2202 /* skip entries for current and parent directory */
2203 if (strEqual(directory_name, ".") ||
2204 strEqual(directory_name, ".."))
2206 free(directory_path);
2210 /* find out if directory entry is itself a directory */
2211 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2212 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2214 free(directory_path);
2218 free(directory_path);
2220 if (strEqual(directory_name, GRAPHICS_DIRECTORY) ||
2221 strEqual(directory_name, SOUNDS_DIRECTORY) ||
2222 strEqual(directory_name, MUSIC_DIRECTORY))
2225 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2232 /* special case: top level directory may directly contain "levelinfo.conf" */
2233 if (node_parent == NULL && !valid_entry_found)
2235 /* check if this directory directly contains a file "levelinfo.conf" */
2236 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
2237 level_directory, ".");
2240 if (!valid_entry_found)
2241 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
2245 boolean AdjustGraphicsForEMC()
2247 boolean settings_changed = FALSE;
2249 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first_all);
2250 settings_changed |= adjustTreeGraphicsForEMC(leveldir_first);
2252 return settings_changed;
2255 void LoadLevelInfo()
2257 InitUserLevelDirectory(getLoginName());
2259 DrawInitText("Loading level series:", 120, FC_GREEN);
2261 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
2262 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
2265 /* after loading all level set information, clone the level directory tree
2266 and remove all level sets without levels (these may still contain artwork
2267 to be offered in the setup menu as "custom artwork", and are therefore
2268 checked for existing artwork in the function "LoadLevelArtworkInfo()") */
2269 leveldir_first_all = leveldir_first;
2270 cloneTree(&leveldir_first, leveldir_first_all, TRUE);
2273 AdjustGraphicsForEMC();
2275 /* before sorting, the first entries will be from the user directory */
2276 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2278 if (leveldir_first == NULL)
2279 Error(ERR_EXIT, "cannot find any valid level series in any directory");
2281 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
2284 dumpTreeInfo(leveldir_first, 0);
2288 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
2289 TreeInfo *node_parent,
2290 char *base_directory,
2291 char *directory_name, int type)
2293 char *directory_path = getPath2(base_directory, directory_name);
2294 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
2295 SetupFileHash *setup_file_hash = NULL;
2296 TreeInfo *artwork_new = NULL;
2299 if (fileExists(filename))
2300 setup_file_hash = loadSetupFileHash(filename);
2302 if (setup_file_hash == NULL) /* no config file -- look for artwork files */
2305 struct dirent *dir_entry;
2306 boolean valid_file_found = FALSE;
2308 if ((dir = opendir(directory_path)) != NULL)
2310 while ((dir_entry = readdir(dir)) != NULL)
2312 char *entry_name = dir_entry->d_name;
2314 if (FileIsArtworkType(entry_name, type))
2316 valid_file_found = TRUE;
2324 if (!valid_file_found)
2326 if (!strEqual(directory_name, "."))
2327 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
2329 free(directory_path);
2336 artwork_new = newTreeInfo();
2339 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
2341 setTreeInfoToDefaults(artwork_new, type);
2343 artwork_new->subdir = getStringCopy(directory_name);
2345 if (setup_file_hash) /* (before defining ".color" and ".class_desc") */
2348 checkSetupFileHashIdentifier(setup_file_hash, filename, getCookie("..."));
2351 /* set all structure fields according to the token/value pairs */
2353 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2354 setSetupInfo(levelinfo_tokens, i,
2355 getHashEntry(setup_file_hash, levelinfo_tokens[i].text));
2358 if (strEqual(artwork_new->name, ANONYMOUS_NAME))
2359 setString(&artwork_new->name, artwork_new->subdir);
2362 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2365 if (artwork_new->identifier == NULL)
2366 artwork_new->identifier = getStringCopy(artwork_new->subdir);
2368 if (artwork_new->name_sorting == NULL)
2369 artwork_new->name_sorting = getStringCopy(artwork_new->name);
2372 if (node_parent == NULL) /* top level group */
2374 artwork_new->basepath = getStringCopy(base_directory);
2375 artwork_new->fullpath = getStringCopy(artwork_new->subdir);
2377 else /* sub level group */
2379 artwork_new->basepath = getStringCopy(node_parent->basepath);
2380 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
2383 artwork_new->in_user_dir =
2384 (!strEqual(artwork_new->basepath, OPTIONS_ARTWORK_DIRECTORY(type)));
2386 /* (may use ".sort_priority" from "setup_file_hash" above) */
2387 artwork_new->color = ARTWORKCOLOR(artwork_new);
2389 setString(&artwork_new->class_desc, getLevelClassDescription(artwork_new));
2391 if (setup_file_hash == NULL) /* (after determining ".user_defined") */
2393 if (strEqual(artwork_new->subdir, "."))
2395 if (artwork_new->user_defined)
2397 setString(&artwork_new->identifier, "private");
2398 artwork_new->sort_priority = ARTWORKCLASS_PRIVATE;
2402 setString(&artwork_new->identifier, "classic");
2403 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
2406 /* set to new values after changing ".sort_priority" */
2407 artwork_new->color = ARTWORKCOLOR(artwork_new);
2409 setString(&artwork_new->class_desc,
2410 getLevelClassDescription(artwork_new));
2414 setString(&artwork_new->identifier, artwork_new->subdir);
2417 setString(&artwork_new->name, artwork_new->identifier);
2418 setString(&artwork_new->name_sorting, artwork_new->name);
2421 DrawInitText(artwork_new->name, 150, FC_YELLOW);
2423 pushTreeInfo(node_first, artwork_new);
2425 freeSetupFileHash(setup_file_hash);
2427 free(directory_path);
2433 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
2434 TreeInfo *node_parent,
2435 char *base_directory, int type)
2438 struct dirent *dir_entry;
2439 boolean valid_entry_found = FALSE;
2441 if ((dir = opendir(base_directory)) == NULL)
2443 /* display error if directory is main "options.graphics_directory" etc. */
2444 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
2445 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
2450 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
2452 struct stat file_status;
2453 char *directory_name = dir_entry->d_name;
2454 char *directory_path = getPath2(base_directory, directory_name);
2456 /* skip directory entries for current and parent directory */
2457 if (strEqual(directory_name, ".") ||
2458 strEqual(directory_name, ".."))
2460 free(directory_path);
2464 /* skip directory entries which are not a directory or are not accessible */
2465 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
2466 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
2468 free(directory_path);
2472 free(directory_path);
2474 /* check if this directory contains artwork with or without config file */
2475 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2477 directory_name, type);
2482 /* check if this directory directly contains artwork itself */
2483 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first, node_parent,
2484 base_directory, ".",
2486 if (!valid_entry_found)
2487 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
2491 static TreeInfo *getDummyArtworkInfo(int type)
2493 /* this is only needed when there is completely no artwork available */
2494 TreeInfo *artwork_new = newTreeInfo();
2496 setTreeInfoToDefaults(artwork_new, type);
2498 setString(&artwork_new->subdir, UNDEFINED_FILENAME);
2499 setString(&artwork_new->fullpath, UNDEFINED_FILENAME);
2500 setString(&artwork_new->basepath, UNDEFINED_FILENAME);
2502 setString(&artwork_new->identifier, UNDEFINED_FILENAME);
2503 setString(&artwork_new->name, UNDEFINED_FILENAME);
2504 setString(&artwork_new->name_sorting, UNDEFINED_FILENAME);
2509 void LoadArtworkInfo()
2511 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
2513 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2514 options.graphics_directory,
2515 TREE_TYPE_GRAPHICS_DIR);
2516 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
2517 getUserGraphicsDir(),
2518 TREE_TYPE_GRAPHICS_DIR);
2520 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2521 options.sounds_directory,
2522 TREE_TYPE_SOUNDS_DIR);
2523 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
2525 TREE_TYPE_SOUNDS_DIR);
2527 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2528 options.music_directory,
2529 TREE_TYPE_MUSIC_DIR);
2530 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
2532 TREE_TYPE_MUSIC_DIR);
2534 if (artwork.gfx_first == NULL)
2535 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
2536 if (artwork.snd_first == NULL)
2537 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
2538 if (artwork.mus_first == NULL)
2539 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
2541 /* before sorting, the first entries will be from the user directory */
2542 artwork.gfx_current =
2543 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2544 if (artwork.gfx_current == NULL)
2545 artwork.gfx_current =
2546 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2547 if (artwork.gfx_current == NULL)
2548 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2550 artwork.snd_current =
2551 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2552 if (artwork.snd_current == NULL)
2553 artwork.snd_current =
2554 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2555 if (artwork.snd_current == NULL)
2556 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2558 artwork.mus_current =
2559 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2560 if (artwork.mus_current == NULL)
2561 artwork.mus_current =
2562 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2563 if (artwork.mus_current == NULL)
2564 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2566 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
2567 artwork.snd_current_identifier = artwork.snd_current->identifier;
2568 artwork.mus_current_identifier = artwork.mus_current->identifier;
2571 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
2572 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
2573 printf("music set == %s\n\n", artwork.mus_current_identifier);
2576 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2577 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2578 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2581 dumpTreeInfo(artwork.gfx_first, 0);
2582 dumpTreeInfo(artwork.snd_first, 0);
2583 dumpTreeInfo(artwork.mus_first, 0);
2587 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
2588 LevelDirTree *level_node)
2590 /* recursively check all level directories for artwork sub-directories */
2594 /* check all tree entries for artwork, but skip parent link entries */
2595 if (!level_node->parent_link)
2597 TreeInfo *topnode_last = *artwork_node;
2598 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
2599 ARTWORK_DIRECTORY((*artwork_node)->type));
2601 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2602 (*artwork_node)->type);
2604 if (topnode_last != *artwork_node)
2606 free((*artwork_node)->identifier);
2607 free((*artwork_node)->name);
2608 free((*artwork_node)->name_sorting);
2610 (*artwork_node)->identifier = getStringCopy(level_node->subdir);
2611 (*artwork_node)->name = getStringCopy(level_node->name);
2612 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2614 (*artwork_node)->sort_priority = level_node->sort_priority;
2615 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2621 if (level_node->node_group != NULL)
2622 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2624 level_node = level_node->next;
2628 void LoadLevelArtworkInfo()
2630 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2632 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first_all);
2633 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first_all);
2634 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first_all);
2636 /* needed for reloading level artwork not known at ealier stage */
2638 if (!strEqual(artwork.gfx_current_identifier, setup.graphics_set))
2640 artwork.gfx_current =
2641 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2642 if (artwork.gfx_current == NULL)
2643 artwork.gfx_current =
2644 getTreeInfoFromIdentifier(artwork.gfx_first, GFX_CLASSIC_SUBDIR);
2645 if (artwork.gfx_current == NULL)
2646 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2649 if (!strEqual(artwork.snd_current_identifier, setup.sounds_set))
2651 artwork.snd_current =
2652 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2653 if (artwork.snd_current == NULL)
2654 artwork.snd_current =
2655 getTreeInfoFromIdentifier(artwork.snd_first, SND_CLASSIC_SUBDIR);
2656 if (artwork.snd_current == NULL)
2657 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2660 if (!strEqual(artwork.mus_current_identifier, setup.music_set))
2662 artwork.mus_current =
2663 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2664 if (artwork.mus_current == NULL)
2665 artwork.mus_current =
2666 getTreeInfoFromIdentifier(artwork.mus_first, MUS_CLASSIC_SUBDIR);
2667 if (artwork.mus_current == NULL)
2668 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2671 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2672 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2673 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2676 dumpTreeInfo(artwork.gfx_first, 0);
2677 dumpTreeInfo(artwork.snd_first, 0);
2678 dumpTreeInfo(artwork.mus_first, 0);
2682 static void SaveUserLevelInfo()
2684 LevelDirTree *level_info;
2689 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2691 if (!(file = fopen(filename, MODE_WRITE)))
2693 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2698 level_info = newTreeInfo();
2700 /* always start with reliable default values */
2701 setTreeInfoToDefaults(level_info, TREE_TYPE_LEVEL_DIR);
2703 setString(&level_info->name, getLoginName());
2704 setString(&level_info->author, getRealName());
2705 level_info->levels = 100;
2706 level_info->first_level = 1;
2708 token_value_position = TOKEN_VALUE_POSITION_SHORT;
2710 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2711 getCookie("LEVELINFO")));
2714 for (i = 0; i < NUM_LEVELINFO_TOKENS; i++)
2716 if (i == LEVELINFO_TOKEN_NAME ||
2717 i == LEVELINFO_TOKEN_AUTHOR ||
2718 i == LEVELINFO_TOKEN_LEVELS ||
2719 i == LEVELINFO_TOKEN_FIRST_LEVEL)
2720 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2722 /* just to make things nicer :) */
2723 if (i == LEVELINFO_TOKEN_AUTHOR)
2724 fprintf(file, "\n");
2727 token_value_position = TOKEN_VALUE_POSITION_DEFAULT;
2731 SetFilePermissions(filename, PERMS_PRIVATE);
2733 freeTreeInfo(level_info);
2737 char *getSetupValue(int type, void *value)
2739 static char value_string[MAX_LINE_LEN];
2747 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2751 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2755 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2759 strcpy(value_string, (*(boolean *)value ? "AGA" : "ECS"));
2763 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2767 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2771 sprintf(value_string, "%d", *(int *)value);
2775 strcpy(value_string, *(char **)value);
2779 value_string[0] = '\0';
2783 if (type & TYPE_GHOSTED)
2784 strcpy(value_string, "n/a");
2786 return value_string;
2789 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2793 static char token_string[MAX_LINE_LEN];
2794 int token_type = token_info[token_nr].type;
2795 void *setup_value = token_info[token_nr].value;
2796 char *token_text = token_info[token_nr].text;
2797 char *value_string = getSetupValue(token_type, setup_value);
2799 /* build complete token string */
2800 sprintf(token_string, "%s%s", prefix, token_text);
2802 /* build setup entry line */
2803 line = getFormattedSetupEntry(token_string, value_string);
2805 if (token_type == TYPE_KEY_X11)
2807 Key key = *(Key *)setup_value;
2808 char *keyname = getKeyNameFromKey(key);
2810 /* add comment, if useful */
2811 if (!strEqual(keyname, "(undefined)") &&
2812 !strEqual(keyname, "(unknown)"))
2814 /* add at least one whitespace */
2816 for (i = strlen(line); i < token_comment_position; i++)
2820 strcat(line, keyname);
2827 void LoadLevelSetup_LastSeries()
2829 /* ----------------------------------------------------------------------- */
2830 /* ~/.<program>/levelsetup.conf */
2831 /* ----------------------------------------------------------------------- */
2833 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2834 SetupFileHash *level_setup_hash = NULL;
2836 /* always start with reliable default values */
2837 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2839 if ((level_setup_hash = loadSetupFileHash(filename)))
2841 char *last_level_series =
2842 getHashEntry(level_setup_hash, TOKEN_STR_LAST_LEVEL_SERIES);
2844 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2846 if (leveldir_current == NULL)
2847 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2849 checkSetupFileHashIdentifier(level_setup_hash, filename,
2850 getCookie("LEVELSETUP"));
2852 freeSetupFileHash(level_setup_hash);
2855 Error(ERR_WARN, "using default setup values");
2860 void SaveLevelSetup_LastSeries()
2862 /* ----------------------------------------------------------------------- */
2863 /* ~/.<program>/levelsetup.conf */
2864 /* ----------------------------------------------------------------------- */
2866 char *filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2867 char *level_subdir = leveldir_current->subdir;
2870 InitUserDataDirectory();
2872 if (!(file = fopen(filename, MODE_WRITE)))
2874 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2879 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2880 getCookie("LEVELSETUP")));
2881 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2886 SetFilePermissions(filename, PERMS_PRIVATE);
2891 static void checkSeriesInfo()
2893 static char *level_directory = NULL;
2895 struct dirent *dir_entry;
2897 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2899 level_directory = getPath2((leveldir_current->in_user_dir ?
2900 getUserLevelDir(NULL) :
2901 options.level_directory),
2902 leveldir_current->fullpath);
2904 if ((dir = opendir(level_directory)) == NULL)
2906 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2910 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2912 if (strlen(dir_entry->d_name) > 4 &&
2913 dir_entry->d_name[3] == '.' &&
2914 strEqual(&dir_entry->d_name[4], LEVELFILE_EXTENSION))
2916 char levelnum_str[4];
2919 strncpy(levelnum_str, dir_entry->d_name, 3);
2920 levelnum_str[3] = '\0';
2922 levelnum_value = atoi(levelnum_str);
2925 if (levelnum_value < leveldir_current->first_level)
2927 Error(ERR_WARN, "additional level %d found", levelnum_value);
2928 leveldir_current->first_level = levelnum_value;
2930 else if (levelnum_value > leveldir_current->last_level)
2932 Error(ERR_WARN, "additional level %d found", levelnum_value);
2933 leveldir_current->last_level = levelnum_value;
2942 void LoadLevelSetup_SeriesInfo()
2945 SetupFileHash *level_setup_hash = NULL;
2946 char *level_subdir = leveldir_current->subdir;
2948 /* always start with reliable default values */
2949 level_nr = leveldir_current->first_level;
2951 checkSeriesInfo(leveldir_current);
2953 /* ----------------------------------------------------------------------- */
2954 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2955 /* ----------------------------------------------------------------------- */
2957 level_subdir = leveldir_current->subdir;
2959 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2961 if ((level_setup_hash = loadSetupFileHash(filename)))
2965 token_value = getHashEntry(level_setup_hash, TOKEN_STR_LAST_PLAYED_LEVEL);
2969 level_nr = atoi(token_value);
2971 if (level_nr < leveldir_current->first_level)
2972 level_nr = leveldir_current->first_level;
2973 if (level_nr > leveldir_current->last_level)
2974 level_nr = leveldir_current->last_level;
2977 token_value = getHashEntry(level_setup_hash, TOKEN_STR_HANDICAP_LEVEL);
2981 int level_nr = atoi(token_value);
2983 if (level_nr < leveldir_current->first_level)
2984 level_nr = leveldir_current->first_level;
2985 if (level_nr > leveldir_current->last_level + 1)
2986 level_nr = leveldir_current->last_level;
2988 if (leveldir_current->user_defined || !leveldir_current->handicap)
2989 level_nr = leveldir_current->last_level;
2991 leveldir_current->handicap_level = level_nr;
2994 checkSetupFileHashIdentifier(level_setup_hash, filename,
2995 getCookie("LEVELSETUP"));
2997 freeSetupFileHash(level_setup_hash);
3000 Error(ERR_WARN, "using default setup values");
3005 void SaveLevelSetup_SeriesInfo()
3008 char *level_subdir = leveldir_current->subdir;
3009 char *level_nr_str = int2str(level_nr, 0);
3010 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
3013 /* ----------------------------------------------------------------------- */
3014 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
3015 /* ----------------------------------------------------------------------- */
3017 InitLevelSetupDirectory(level_subdir);
3019 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
3021 if (!(file = fopen(filename, MODE_WRITE)))
3023 Error(ERR_WARN, "cannot write setup file '%s'", filename);
3028 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
3029 getCookie("LEVELSETUP")));
3030 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
3032 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
3033 handicap_level_str));
3037 SetFilePermissions(filename, PERMS_PRIVATE);