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>
25 /* file names and filename extensions */
26 #if !defined(PLATFORM_MSDOS)
27 #define LEVELSETUP_DIRECTORY "levelsetup"
28 #define SETUP_FILENAME "setup.conf"
29 #define LEVELSETUP_FILENAME "levelsetup.conf"
30 #define LEVELINFO_FILENAME "levelinfo.conf"
31 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
32 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
33 #define MUSICINFO_FILENAME "musicinfo.conf"
34 #define LEVELFILE_EXTENSION "level"
35 #define TAPEFILE_EXTENSION "tape"
36 #define SCOREFILE_EXTENSION "score"
38 #define LEVELSETUP_DIRECTORY "lvlsetup"
39 #define SETUP_FILENAME "setup.cnf"
40 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
41 #define LEVELINFO_FILENAME "lvlinfo.cnf"
42 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
43 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
44 #define MUSICINFO_FILENAME "musinfo.cnf"
45 #define LEVELFILE_EXTENSION "lvl"
46 #define TAPEFILE_EXTENSION "tap"
47 #define SCOREFILE_EXTENSION "sco"
50 #define NUM_LEVELCLASS_DESC 8
51 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
63 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
64 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
65 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
66 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
67 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
68 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
69 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
70 IS_LEVELCLASS_USER(n) ? FC_RED : \
73 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
74 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
75 IS_LEVELCLASS_BD(n) ? 2 : \
76 IS_LEVELCLASS_EM(n) ? 3 : \
77 IS_LEVELCLASS_SP(n) ? 4 : \
78 IS_LEVELCLASS_DX(n) ? 5 : \
79 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
80 IS_LEVELCLASS_USER(n) ? 7 : \
83 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
84 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
85 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
86 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
89 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
90 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
91 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
92 IS_ARTWORKCLASS_USER(n) ? 3 : \
95 #define TOKEN_VALUE_POSITION 40
96 #define TOKEN_COMMENT_POSITION 60
98 #define MAX_COOKIE_LEN 256
100 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
101 GRAPHICSINFO_FILENAME : \
102 (type) == TREE_TYPE_SOUNDS_DIR ? \
103 SOUNDSINFO_FILENAME : \
104 (type) == TREE_TYPE_MUSIC_DIR ? \
105 MUSICINFO_FILENAME : "")
107 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
108 GRAPHICS_DIRECTORY : \
109 (type) == TREE_TYPE_SOUNDS_DIR ? \
111 (type) == TREE_TYPE_MUSIC_DIR ? \
112 MUSIC_DIRECTORY : "")
114 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
115 options.graphics_directory : \
116 (type) == TREE_TYPE_SOUNDS_DIR ? \
117 options.sounds_directory : \
118 (type) == TREE_TYPE_MUSIC_DIR ? \
119 options.music_directory : "")
122 /* ------------------------------------------------------------------------- */
124 /* ------------------------------------------------------------------------- */
126 static char *getLevelClassDescription(TreeInfo *ldi)
128 int position = ldi->sort_priority / 100;
130 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
131 return levelclass_desc[position];
133 return "Unknown Level Class";
136 static char *getUserLevelDir(char *level_subdir)
138 static char *userlevel_dir = NULL;
139 char *data_dir = getUserDataDir();
140 char *userlevel_subdir = LEVELS_DIRECTORY;
145 if (level_subdir != NULL)
146 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
148 userlevel_dir = getPath2(data_dir, userlevel_subdir);
150 return userlevel_dir;
153 static char *getTapeDir(char *level_subdir)
155 static char *tape_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *tape_subdir = TAPES_DIRECTORY;
162 if (level_subdir != NULL)
163 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
165 tape_dir = getPath2(data_dir, tape_subdir);
170 static char *getScoreDir(char *level_subdir)
172 static char *score_dir = NULL;
173 char *data_dir = options.rw_base_directory;
174 char *score_subdir = SCORES_DIRECTORY;
179 if (level_subdir != NULL)
180 score_dir = getPath3(data_dir, score_subdir, level_subdir);
182 score_dir = getPath2(data_dir, score_subdir);
187 static char *getLevelSetupDir(char *level_subdir)
189 static char *levelsetup_dir = NULL;
190 char *data_dir = getUserDataDir();
191 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
194 free(levelsetup_dir);
196 if (level_subdir != NULL)
197 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
199 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
201 return levelsetup_dir;
204 static char *getLevelDirFromTreeInfo(TreeInfo *node)
206 static char *level_dir = NULL;
209 return options.level_directory;
214 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
215 options.level_directory), node->fullpath);
220 static char *getCurrentLevelDir()
222 return getLevelDirFromTreeInfo(leveldir_current);
225 static char *getDefaultGraphicsDir(char *graphics_subdir)
227 static char *graphics_dir = NULL;
229 if (graphics_subdir == NULL)
230 return options.graphics_directory;
235 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
240 static char *getDefaultSoundsDir(char *sounds_subdir)
242 static char *sounds_dir = NULL;
244 if (sounds_subdir == NULL)
245 return options.sounds_directory;
250 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
255 static char *getDefaultMusicDir(char *music_subdir)
257 static char *music_dir = NULL;
259 if (music_subdir == NULL)
260 return options.music_directory;
265 music_dir = getPath2(options.music_directory, music_subdir);
270 static char *getUserGraphicsDir()
272 static char *usergraphics_dir = NULL;
274 if (usergraphics_dir == NULL)
275 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
277 return usergraphics_dir;
280 static char *getUserSoundsDir()
282 static char *usersounds_dir = NULL;
284 if (usersounds_dir == NULL)
285 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
287 return usersounds_dir;
290 static char *getUserMusicDir()
292 static char *usermusic_dir = NULL;
294 if (usermusic_dir == NULL)
295 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
297 return usermusic_dir;
300 static char *getSetupArtworkDir(TreeInfo *ti)
302 static char *artwork_dir = NULL;
304 if (artwork_dir != NULL)
307 artwork_dir = getPath2(ti->basepath, ti->fullpath);
312 void setLevelArtworkDir(TreeInfo *ti)
314 char **artwork_path_ptr, *artwork_set;
315 TreeInfo *level_artwork;
317 if (ti == NULL || leveldir_current == NULL)
321 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
322 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
323 &leveldir_current->music_path);
326 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
327 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
328 leveldir_current->music_set);
330 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)) == NULL)
333 if (*artwork_path_ptr != NULL)
334 free(*artwork_path_ptr);
336 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
339 static char *getLevelArtworkDir(int type)
343 if (leveldir_current == NULL)
344 return UNDEFINED_FILENAME;
347 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
348 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
349 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
355 char *getLevelFilename(int nr)
357 static char *filename = NULL;
358 char basename[MAX_FILENAME_LEN];
360 if (filename != NULL)
363 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
364 filename = getPath2(getCurrentLevelDir(), basename);
369 char *getTapeFilename(int nr)
371 static char *filename = NULL;
372 char basename[MAX_FILENAME_LEN];
374 if (filename != NULL)
377 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
378 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
383 char *getScoreFilename(int nr)
385 static char *filename = NULL;
386 char basename[MAX_FILENAME_LEN];
388 if (filename != NULL)
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 if (filename != NULL)
404 filename = getPath2(getSetupDir(), SETUP_FILENAME);
409 static char *getCorrectedImageBasename(char *basename)
411 char *basename_corrected = basename;
413 #if defined(PLATFORM_MSDOS)
414 if (program.filename_prefix != NULL)
416 int prefix_len = strlen(program.filename_prefix);
418 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
419 basename_corrected = &basename[prefix_len];
421 /* if corrected filename is still longer than standard MS-DOS filename
422 size (8 characters + 1 dot + 3 characters file extension), shorten
423 filename by writing file extension after 8th basename character */
424 if (strlen(basename_corrected) > 8+1+3)
426 static char *msdos_filename = NULL;
428 if (filename != NULL)
431 filename = getStringCopy(basename_corrected);
432 strncpy(&filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
437 return basename_corrected;
440 static boolean fileExists(char *filename)
443 printf("checking file '%s'\n", filename);
446 return (access(filename, F_OK) == 0);
449 char *getCustomImageFilename(char *basename)
451 static char *filename = NULL;
453 if (filename != NULL)
456 basename = getCorrectedImageBasename(basename);
458 if (!setup.override_level_graphics)
460 /* 1st try: look for special artwork configured in level series config */
461 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
462 if (fileExists(filename))
465 /* 2nd try: look for special artwork in current level series directory */
466 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
467 if (fileExists(filename))
471 /* 3rd try: look for special artwork in configured artwork directory */
472 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
473 if (fileExists(filename))
476 /* 4th try: look for default artwork in new default artwork directory */
477 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
478 if (fileExists(filename))
481 /* 5th try: look for default artwork in old default artwork directory */
482 filename = getPath2(options.graphics_directory, basename);
483 if (fileExists(filename))
486 return NULL; /* cannot find specified artwork file anywhere */
489 char *getCustomSoundFilename(char *basename)
491 static char *filename = NULL;
493 if (filename != NULL)
496 if (!setup.override_level_sounds)
498 /* 1st try: look for special artwork configured in level series config */
499 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
500 if (fileExists(filename))
503 /* 2nd try: look for special artwork in current level series directory */
504 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
505 if (fileExists(filename))
509 /* 3rd try: look for special artwork in configured artwork directory */
510 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
511 if (fileExists(filename))
514 /* 4th try: look for default artwork in new default artwork directory */
515 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
516 if (fileExists(filename))
519 /* 5th try: look for default artwork in old default artwork directory */
520 filename = getPath2(options.sounds_directory, basename);
521 if (fileExists(filename))
524 return NULL; /* cannot find specified artwork file anywhere */
527 char *getCustomArtworkFilename(char *basename, int type)
529 if (type == ARTWORK_TYPE_GRAPHICS)
530 return getCustomImageFilename(basename);
531 else if (type == ARTWORK_TYPE_SOUNDS)
532 return getCustomSoundFilename(basename);
534 return UNDEFINED_FILENAME;
537 char *getCustomArtworkConfigFilename(int type)
539 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
542 char *getCustomMusicDirectory(void)
544 static char *directory = NULL;
546 if (directory != NULL)
549 if (!setup.override_level_music)
551 /* 1st try: look for special artwork configured in level series config */
552 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
553 if (fileExists(directory))
556 /* 2nd try: look for special artwork in current level series directory */
557 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
558 if (fileExists(directory))
562 /* 3rd try: look for special artwork in configured artwork directory */
563 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
564 if (fileExists(directory))
567 /* 4th try: look for default artwork in new default artwork directory */
568 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
569 if (fileExists(directory))
572 /* 5th try: look for default artwork in old default artwork directory */
573 directory = getStringCopy(options.music_directory);
574 if (fileExists(directory))
577 return NULL; /* cannot find specified artwork file anywhere */
580 void InitTapeDirectory(char *level_subdir)
582 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
583 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
584 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
587 void InitScoreDirectory(char *level_subdir)
589 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
590 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
593 static void SaveUserLevelInfo();
595 void InitUserLevelDirectory(char *level_subdir)
597 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
599 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
600 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
601 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
607 void InitLevelSetupDirectory(char *level_subdir)
609 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
610 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
611 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
615 /* ------------------------------------------------------------------------- */
616 /* some functions to handle lists of level directories */
617 /* ------------------------------------------------------------------------- */
619 TreeInfo *newTreeInfo()
621 return checked_calloc(sizeof(TreeInfo));
624 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
626 node_new->next = *node_first;
627 *node_first = node_new;
630 int numTreeInfo(TreeInfo *node)
643 boolean validLevelSeries(TreeInfo *node)
645 return (node != NULL && !node->node_group && !node->parent_link);
648 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
653 if (node->node_group) /* enter level group (step down into tree) */
654 return getFirstValidTreeInfoEntry(node->node_group);
655 else if (node->parent_link) /* skip start entry of level group */
657 if (node->next) /* get first real level series entry */
658 return getFirstValidTreeInfoEntry(node->next);
659 else /* leave empty level group and go on */
660 return getFirstValidTreeInfoEntry(node->node_parent->next);
662 else /* this seems to be a regular level series */
666 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
671 if (node->node_parent == NULL) /* top level group */
672 return *node->node_top;
673 else /* sub level group */
674 return node->node_parent->node_group;
677 int numTreeInfoInGroup(TreeInfo *node)
679 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
682 int posTreeInfo(TreeInfo *node)
684 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
689 if (node_cmp == node)
693 node_cmp = node_cmp->next;
699 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
701 TreeInfo *node_default = node;
716 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
718 if (identifier == NULL)
723 if (node->node_group)
725 TreeInfo *node_group;
727 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
732 else if (!node->parent_link)
734 if (strcmp(identifier, node->identifier) == 0)
744 void dumpTreeInfo(TreeInfo *node, int depth)
748 printf("Dumping TreeInfo:\n");
752 for (i=0; i<(depth + 1) * 3; i++)
755 printf("filename == '%s' (%s) [%s] (%d)\n",
756 node->filename, node->name, node->identifier, node->sort_priority);
758 if (node->node_group != NULL)
759 dumpTreeInfo(node->node_group, depth + 1);
765 void sortTreeInfo(TreeInfo **node_first,
766 int (*compare_function)(const void *, const void *))
768 int num_nodes = numTreeInfo(*node_first);
769 TreeInfo **sort_array;
770 TreeInfo *node = *node_first;
776 /* allocate array for sorting structure pointers */
777 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
779 /* writing structure pointers to sorting array */
780 while (i < num_nodes && node) /* double boundary check... */
782 sort_array[i] = node;
788 /* sorting the structure pointers in the sorting array */
789 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
792 /* update the linkage of list elements with the sorted node array */
793 for (i=0; i<num_nodes - 1; i++)
794 sort_array[i]->next = sort_array[i + 1];
795 sort_array[num_nodes - 1]->next = NULL;
797 /* update the linkage of the main list anchor pointer */
798 *node_first = sort_array[0];
802 /* now recursively sort the level group structures */
806 if (node->node_group != NULL)
807 sortTreeInfo(&node->node_group, compare_function);
814 /* ========================================================================= */
815 /* some stuff from "files.c" */
816 /* ========================================================================= */
818 #if defined(PLATFORM_WIN32)
820 #define S_IRGRP S_IRUSR
823 #define S_IROTH S_IRUSR
826 #define S_IWGRP S_IWUSR
829 #define S_IWOTH S_IWUSR
832 #define S_IXGRP S_IXUSR
835 #define S_IXOTH S_IXUSR
838 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
843 #endif /* PLATFORM_WIN32 */
845 /* file permissions for newly written files */
846 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
847 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
848 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
850 #define MODE_W_PRIVATE (S_IWUSR)
851 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
852 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
854 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
855 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
857 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
858 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
860 char *getUserDataDir(void)
862 static char *userdata_dir = NULL;
866 char *home_dir = getHomeDir();
867 char *data_dir = program.userdata_directory;
869 userdata_dir = getPath2(home_dir, data_dir);
877 return getUserDataDir();
880 static mode_t posix_umask(mode_t mask)
882 #if defined(PLATFORM_UNIX)
889 static int posix_mkdir(const char *pathname, mode_t mode)
891 #if defined(PLATFORM_WIN32)
892 return mkdir(pathname);
894 return mkdir(pathname, mode);
898 void createDirectory(char *dir, char *text, int permission_class)
900 /* leave "other" permissions in umask untouched, but ensure group parts
901 of USERDATA_DIR_MODE are not masked */
902 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
903 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
904 mode_t normal_umask = posix_umask(0);
905 mode_t group_umask = ~(dir_mode & S_IRWXG);
906 posix_umask(normal_umask & group_umask);
908 if (access(dir, F_OK) != 0)
909 if (posix_mkdir(dir, dir_mode) != 0)
910 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
912 posix_umask(normal_umask); /* reset normal umask */
915 void InitUserDataDirectory()
917 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
920 void SetFilePermissions(char *filename, int permission_class)
922 chmod(filename, (permission_class == PERMS_PRIVATE ?
923 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
926 char *getCookie(char *file_type)
928 static char cookie[MAX_COOKIE_LEN + 1];
930 if (strlen(program.cookie_prefix) + 1 +
931 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
932 return "[COOKIE ERROR]"; /* should never happen */
934 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
935 program.cookie_prefix, file_type,
936 program.version_major, program.version_minor);
941 int getFileVersionFromCookieString(const char *cookie)
943 const char *ptr_cookie1, *ptr_cookie2;
944 const char *pattern1 = "_FILE_VERSION_";
945 const char *pattern2 = "?.?";
946 const int len_cookie = strlen(cookie);
947 const int len_pattern1 = strlen(pattern1);
948 const int len_pattern2 = strlen(pattern2);
949 const int len_pattern = len_pattern1 + len_pattern2;
950 int version_major, version_minor;
952 if (len_cookie <= len_pattern)
955 ptr_cookie1 = &cookie[len_cookie - len_pattern];
956 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
958 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
961 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
962 ptr_cookie2[1] != '.' ||
963 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
966 version_major = ptr_cookie2[0] - '0';
967 version_minor = ptr_cookie2[2] - '0';
969 return VERSION_IDENT(version_major, version_minor, 0);
972 boolean checkCookieString(const char *cookie, const char *template)
974 const char *pattern = "_FILE_VERSION_?.?";
975 const int len_cookie = strlen(cookie);
976 const int len_template = strlen(template);
977 const int len_pattern = strlen(pattern);
979 if (len_cookie != len_template)
982 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
988 /* ------------------------------------------------------------------------- */
989 /* setup file list handling functions */
990 /* ------------------------------------------------------------------------- */
992 int get_string_integer_value(char *s)
994 static char *number_text[][3] =
996 { "0", "zero", "null", },
997 { "1", "one", "first" },
998 { "2", "two", "second" },
999 { "3", "three", "third" },
1000 { "4", "four", "fourth" },
1001 { "5", "five", "fifth" },
1002 { "6", "six", "sixth" },
1003 { "7", "seven", "seventh" },
1004 { "8", "eight", "eighth" },
1005 { "9", "nine", "ninth" },
1006 { "10", "ten", "tenth" },
1007 { "11", "eleven", "eleventh" },
1008 { "12", "twelve", "twelfth" },
1012 char *s_lower = getStringToLower(s);
1015 for (i=0; i<13; i++)
1017 if (strcmp(s_lower, number_text[i][j]) == 0)
1028 boolean get_string_boolean_value(char *s)
1030 char *s_lower = getStringToLower(s);
1031 boolean result = FALSE;
1033 if (strcmp(s_lower, "true") == 0 ||
1034 strcmp(s_lower, "yes") == 0 ||
1035 strcmp(s_lower, "on") == 0 ||
1036 get_string_integer_value(s) == 1)
1044 char *getFormattedSetupEntry(char *token, char *value)
1047 static char entry[MAX_LINE_LEN];
1049 /* start with the token and some spaces to format output line */
1050 sprintf(entry, "%s:", token);
1051 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1054 /* continue with the token's value */
1055 strcat(entry, value);
1060 void freeSetupFileList(struct SetupFileList *setup_file_list)
1062 if (!setup_file_list)
1065 if (setup_file_list->token)
1066 free(setup_file_list->token);
1067 if (setup_file_list->value)
1068 free(setup_file_list->value);
1069 if (setup_file_list->next)
1070 freeSetupFileList(setup_file_list->next);
1071 free(setup_file_list);
1074 static struct SetupFileList *newSetupFileList(char *token, char *value)
1076 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1078 new->token = checked_malloc(strlen(token) + 1);
1079 strcpy(new->token, token);
1081 new->value = checked_malloc(strlen(value) + 1);
1082 strcpy(new->value, value);
1089 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1091 if (!setup_file_list)
1094 if (strcmp(setup_file_list->token, token) == 0)
1095 return setup_file_list->value;
1097 return getTokenValue(setup_file_list->next, token);
1100 static void setTokenValue(struct SetupFileList *setup_file_list,
1101 char *token, char *value)
1103 if (!setup_file_list)
1106 if (strcmp(setup_file_list->token, token) == 0)
1108 free(setup_file_list->value);
1109 setup_file_list->value = checked_malloc(strlen(value) + 1);
1110 strcpy(setup_file_list->value, value);
1112 else if (setup_file_list->next == NULL)
1113 setup_file_list->next = newSetupFileList(token, value);
1115 setTokenValue(setup_file_list->next, token, value);
1119 static void printSetupFileList(struct SetupFileList *setup_file_list)
1121 if (!setup_file_list)
1124 printf("token: '%s'\n", setup_file_list->token);
1125 printf("value: '%s'\n", setup_file_list->value);
1127 printSetupFileList(setup_file_list->next);
1131 struct SetupFileList *loadSetupFileList(char *filename)
1134 char line[MAX_LINE_LEN];
1135 char *token, *value, *line_ptr;
1136 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1137 struct SetupFileList *first_valid_list_entry;
1141 if (!(file = fopen(filename, MODE_READ)))
1143 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1149 /* read next line of input file */
1150 if (!fgets(line, MAX_LINE_LEN, file))
1153 /* cut trailing comment or whitespace from input line */
1154 for (line_ptr = line; *line_ptr; line_ptr++)
1156 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1163 /* cut trailing whitespaces from input line */
1164 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1165 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1168 /* ignore empty lines */
1172 line_len = strlen(line);
1174 /* cut leading whitespaces from token */
1175 for (token = line; *token; token++)
1176 if (*token != ' ' && *token != '\t')
1179 /* find end of token */
1180 for (line_ptr = token; *line_ptr; line_ptr++)
1182 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1189 if (line_ptr < line + line_len)
1190 value = line_ptr + 1;
1194 /* cut leading whitespaces from value */
1195 for (; *value; value++)
1196 if (*value != ' ' && *value != '\t')
1199 if (*token && *value)
1200 setTokenValue(setup_file_list, token, value);
1205 first_valid_list_entry = setup_file_list->next;
1207 /* free empty list header */
1208 setup_file_list->next = NULL;
1209 freeSetupFileList(setup_file_list);
1211 if (first_valid_list_entry == NULL)
1212 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1214 return first_valid_list_entry;
1217 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1220 if (!setup_file_list)
1223 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1225 if (!checkCookieString(setup_file_list->value, identifier))
1227 Error(ERR_WARN, "configuration file has wrong file identifier");
1234 if (setup_file_list->next)
1235 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1238 Error(ERR_WARN, "configuration file has no file identifier");
1244 /* ========================================================================= */
1245 /* setup file stuff */
1246 /* ========================================================================= */
1248 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1249 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1250 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1252 /* level directory info */
1253 #define LEVELINFO_TOKEN_IDENTIFIER 0
1254 #define LEVELINFO_TOKEN_NAME 1
1255 #define LEVELINFO_TOKEN_NAME_SORTING 2
1256 #define LEVELINFO_TOKEN_AUTHOR 3
1257 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1258 #define LEVELINFO_TOKEN_LEVELS 5
1259 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1260 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1261 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1262 #define LEVELINFO_TOKEN_READONLY 9
1263 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1264 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1265 #define LEVELINFO_TOKEN_MUSIC_SET 12
1267 #define NUM_LEVELINFO_TOKENS 13
1269 static LevelDirTree ldi;
1271 static struct TokenInfo levelinfo_tokens[] =
1273 /* level directory info */
1274 { TYPE_STRING, &ldi.identifier, "identifier" },
1275 { TYPE_STRING, &ldi.name, "name" },
1276 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1277 { TYPE_STRING, &ldi.author, "author" },
1278 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1279 { TYPE_INTEGER, &ldi.levels, "levels" },
1280 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1281 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1282 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1283 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1284 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1285 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1286 { TYPE_STRING, &ldi.music_set, "music_set" }
1289 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1293 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1294 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1295 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1296 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1299 ldi->node_parent = NULL;
1300 ldi->node_group = NULL;
1304 ldi->cl_cursor = -1;
1306 ldi->filename = NULL;
1307 ldi->fullpath = NULL;
1308 ldi->basepath = NULL;
1309 ldi->identifier = NULL;
1310 ldi->name = getStringCopy(ANONYMOUS_NAME);
1311 ldi->name_sorting = NULL;
1312 ldi->author = getStringCopy(ANONYMOUS_NAME);
1314 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1315 ldi->parent_link = FALSE;
1316 ldi->user_defined = FALSE;
1318 ldi->class_desc = NULL;
1320 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1322 ldi->imported_from = NULL;
1323 ldi->graphics_set = NULL;
1324 ldi->sounds_set = NULL;
1325 ldi->music_set = NULL;
1326 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1327 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1328 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1330 ldi->first_level = 0;
1331 ldi->last_level = 0;
1332 ldi->level_group = FALSE;
1333 ldi->handicap_level = 0;
1334 ldi->readonly = TRUE;
1338 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1342 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1344 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1348 /* first copy all values from the parent structure ... */
1351 /* ... then set all fields to default that cannot be inherited from parent.
1352 This is especially important for all those fields that can be set from
1353 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1354 calls 'free()' for all already set token values which requires that no
1355 other structure's pointer may point to them!
1358 ldi->filename = NULL;
1359 ldi->fullpath = NULL;
1360 ldi->basepath = NULL;
1361 ldi->identifier = NULL;
1362 ldi->name = getStringCopy(ANONYMOUS_NAME);
1363 ldi->name_sorting = NULL;
1364 ldi->author = getStringCopy(parent->author);
1365 ldi->imported_from = getStringCopy(parent->imported_from);
1367 ldi->level_group = FALSE;
1368 ldi->parent_link = FALSE;
1370 ldi->node_top = parent->node_top;
1371 ldi->node_parent = parent;
1372 ldi->node_group = NULL;
1376 void setSetupInfo(struct TokenInfo *token_info,
1377 int token_nr, char *token_value)
1379 int token_type = token_info[token_nr].type;
1380 void *setup_value = token_info[token_nr].value;
1382 if (token_value == NULL)
1385 /* set setup field to corresponding token value */
1390 *(boolean *)setup_value = get_string_boolean_value(token_value);
1394 *(Key *)setup_value = getKeyFromKeyName(token_value);
1398 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1402 *(int *)setup_value = get_string_integer_value(token_value);
1406 if (*(char **)setup_value != NULL)
1407 free(*(char **)setup_value);
1408 *(char **)setup_value = getStringCopy(token_value);
1416 static int compareTreeInfoEntries(const void *object1, const void *object2)
1418 const TreeInfo *entry1 = *((TreeInfo **)object1);
1419 const TreeInfo *entry2 = *((TreeInfo **)object2);
1420 int class_sorting1, class_sorting2;
1423 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1425 class_sorting1 = LEVELSORTING(entry1);
1426 class_sorting2 = LEVELSORTING(entry2);
1430 class_sorting1 = ARTWORKSORTING(entry1);
1431 class_sorting2 = ARTWORKSORTING(entry2);
1434 if (entry1->parent_link || entry2->parent_link)
1435 compare_result = (entry1->parent_link ? -1 : +1);
1436 else if (entry1->sort_priority == entry2->sort_priority)
1438 char *name1 = getStringToLower(entry1->name_sorting);
1439 char *name2 = getStringToLower(entry2->name_sorting);
1441 compare_result = strcmp(name1, name2);
1446 else if (class_sorting1 == class_sorting2)
1447 compare_result = entry1->sort_priority - entry2->sort_priority;
1449 compare_result = class_sorting1 - class_sorting2;
1451 return compare_result;
1454 static void createParentTreeInfoNode(TreeInfo *node_parent)
1458 if (node_parent == NULL)
1461 ti_new = newTreeInfo();
1462 setTreeInfoToDefaults(ti_new, node_parent->type);
1464 ti_new->node_parent = node_parent;
1465 ti_new->parent_link = TRUE;
1467 ti_new->identifier = getStringCopy(node_parent->identifier);
1468 ti_new->name = ".. (parent directory)";
1469 ti_new->name_sorting = getStringCopy(ti_new->name);
1471 ti_new->filename = "..";
1472 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1474 ti_new->sort_priority = node_parent->sort_priority;
1475 ti_new->class_desc = getLevelClassDescription(ti_new);
1477 pushTreeInfo(&node_parent->node_group, ti_new);
1480 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1481 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1483 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1484 TreeInfo *node_parent,
1485 char *level_directory,
1486 char *directory_name)
1488 char *directory_path = getPath2(level_directory, directory_name);
1489 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1490 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1491 LevelDirTree *leveldir_new = NULL;
1494 if (setup_file_list == NULL)
1496 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1498 free(directory_path);
1504 leveldir_new = newTreeInfo();
1507 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1509 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1511 leveldir_new->filename = getStringCopy(directory_name);
1513 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1515 /* set all structure fields according to the token/value pairs */
1516 ldi = *leveldir_new;
1517 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1518 setSetupInfo(levelinfo_tokens, i,
1519 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1520 *leveldir_new = ldi;
1522 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1524 free(leveldir_new->name);
1525 leveldir_new->name = getStringCopy(leveldir_new->filename);
1528 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1530 if (leveldir_new->identifier == NULL)
1531 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1533 if (leveldir_new->name_sorting == NULL)
1534 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1536 if (node_parent == NULL) /* top level group */
1538 leveldir_new->basepath = level_directory;
1539 leveldir_new->fullpath = leveldir_new->filename;
1541 else /* sub level group */
1543 leveldir_new->basepath = node_parent->basepath;
1544 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1547 if (leveldir_new->levels < 1)
1548 leveldir_new->levels = 1;
1550 leveldir_new->last_level =
1551 leveldir_new->first_level + leveldir_new->levels - 1;
1553 leveldir_new->user_defined =
1554 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1556 leveldir_new->color = LEVELCOLOR(leveldir_new);
1557 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1559 leveldir_new->handicap_level = /* set handicap to default value */
1560 (leveldir_new->user_defined ?
1561 leveldir_new->last_level :
1562 leveldir_new->first_level);
1564 pushTreeInfo(node_first, leveldir_new);
1566 freeSetupFileList(setup_file_list);
1568 if (leveldir_new->level_group)
1570 /* create node to link back to current level directory */
1571 createParentTreeInfoNode(leveldir_new);
1573 /* step into sub-directory and look for more level series */
1574 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1575 leveldir_new, directory_path);
1578 free(directory_path);
1584 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1585 TreeInfo *node_parent,
1586 char *level_directory)
1589 struct dirent *dir_entry;
1590 boolean valid_entry_found = FALSE;
1592 if ((dir = opendir(level_directory)) == NULL)
1594 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1598 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1600 struct stat file_status;
1601 char *directory_name = dir_entry->d_name;
1602 char *directory_path = getPath2(level_directory, directory_name);
1604 /* skip entries for current and parent directory */
1605 if (strcmp(directory_name, ".") == 0 ||
1606 strcmp(directory_name, "..") == 0)
1608 free(directory_path);
1612 /* find out if directory entry is itself a directory */
1613 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1614 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1616 free(directory_path);
1620 free(directory_path);
1622 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1623 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1624 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1627 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1634 if (!valid_entry_found)
1636 /* check if this directory directly contains a file "levelinfo.conf" */
1637 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1638 level_directory, ".");
1641 if (!valid_entry_found)
1642 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1646 void LoadLevelInfo()
1648 InitUserLevelDirectory(getLoginName());
1650 DrawInitText("Loading level series:", 120, FC_GREEN);
1652 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1653 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1655 /* before sorting, the first entries will be from the user directory */
1656 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1658 if (leveldir_first == NULL)
1659 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1661 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1664 dumpTreeInfo(leveldir_first, 0);
1668 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1669 TreeInfo *node_parent,
1670 char *base_directory,
1671 char *directory_name, int type)
1673 char *directory_path = getPath2(base_directory, directory_name);
1674 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1675 struct SetupFileList *setup_file_list = NULL;
1676 TreeInfo *artwork_new = NULL;
1679 if (access(filename, F_OK) == 0) /* file exists */
1680 setup_file_list = loadSetupFileList(filename);
1682 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1685 struct dirent *dir_entry;
1686 boolean valid_file_found = FALSE;
1688 if ((dir = opendir(directory_path)) != NULL)
1690 while ((dir_entry = readdir(dir)) != NULL)
1692 char *entry_name = dir_entry->d_name;
1694 if (FileIsArtworkType(entry_name, type))
1696 valid_file_found = TRUE;
1704 if (!valid_file_found)
1706 if (strcmp(directory_name, ".") != 0)
1707 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1709 free(directory_path);
1716 artwork_new = newTreeInfo();
1719 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1721 setTreeInfoToDefaults(artwork_new, type);
1723 artwork_new->filename = getStringCopy(directory_name);
1725 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1728 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1731 /* set all structure fields according to the token/value pairs */
1733 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1734 setSetupInfo(levelinfo_tokens, i,
1735 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1738 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1740 free(artwork_new->name);
1741 artwork_new->name = getStringCopy(artwork_new->filename);
1745 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1748 if (artwork_new->identifier == NULL)
1749 artwork_new->identifier = getStringCopy(artwork_new->filename);
1751 if (artwork_new->name_sorting == NULL)
1752 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1755 if (node_parent == NULL) /* top level group */
1757 artwork_new->basepath = getStringCopy(base_directory);
1758 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1760 else /* sub level group */
1762 artwork_new->basepath = getStringCopy(node_parent->basepath);
1763 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1766 artwork_new->user_defined =
1767 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1769 /* (may use ".sort_priority" from "setup_file_list" above) */
1770 artwork_new->color = ARTWORKCOLOR(artwork_new);
1771 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1773 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1775 if (artwork_new->name != NULL)
1776 free(artwork_new->name);
1778 if (strcmp(artwork_new->filename, ".") == 0)
1780 if (artwork_new->user_defined)
1782 artwork_new->identifier = getStringCopy("private");
1783 artwork_new->sort_priority = ARTWORKCLASS_USER;
1787 artwork_new->identifier = getStringCopy("classic");
1788 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1791 /* set to new values after changing ".sort_priority" */
1792 artwork_new->color = ARTWORKCOLOR(artwork_new);
1793 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1797 artwork_new->identifier = getStringCopy(artwork_new->filename);
1800 artwork_new->name = getStringCopy(artwork_new->identifier);
1801 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1804 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1806 pushTreeInfo(node_first, artwork_new);
1808 freeSetupFileList(setup_file_list);
1810 free(directory_path);
1816 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1817 TreeInfo *node_parent,
1818 char *base_directory, int type)
1821 struct dirent *dir_entry;
1822 boolean valid_entry_found = FALSE;
1824 if ((dir = opendir(base_directory)) == NULL)
1826 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1827 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1831 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1833 struct stat file_status;
1834 char *directory_name = dir_entry->d_name;
1835 char *directory_path = getPath2(base_directory, directory_name);
1837 /* skip entries for current and parent directory */
1838 if (strcmp(directory_name, ".") == 0 ||
1839 strcmp(directory_name, "..") == 0)
1841 free(directory_path);
1845 /* find out if directory entry is itself a directory */
1846 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1847 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1849 free(directory_path);
1853 free(directory_path);
1855 /* check if this directory contains artwork with or without config file */
1856 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1858 directory_name, type);
1863 /* check if this directory directly contains artwork itself */
1864 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1865 base_directory, ".",
1867 if (!valid_entry_found)
1868 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1872 static TreeInfo *getDummyArtworkInfo(int type)
1874 /* this is only needed when there is completely no artwork available */
1875 TreeInfo *artwork_new = newTreeInfo();
1877 setTreeInfoToDefaults(artwork_new, type);
1879 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1880 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1881 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1883 if (artwork_new->name != NULL)
1884 free(artwork_new->name);
1886 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1887 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1888 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1893 void LoadArtworkInfo()
1895 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1897 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1898 options.graphics_directory,
1899 TREE_TYPE_GRAPHICS_DIR);
1900 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1901 getUserGraphicsDir(),
1902 TREE_TYPE_GRAPHICS_DIR);
1904 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1905 options.sounds_directory,
1906 TREE_TYPE_SOUNDS_DIR);
1907 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1909 TREE_TYPE_SOUNDS_DIR);
1911 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1912 options.music_directory,
1913 TREE_TYPE_MUSIC_DIR);
1914 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1916 TREE_TYPE_MUSIC_DIR);
1918 if (artwork.gfx_first == NULL)
1919 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1920 if (artwork.snd_first == NULL)
1921 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1922 if (artwork.mus_first == NULL)
1923 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1925 /* before sorting, the first entries will be from the user directory */
1926 artwork.gfx_current =
1927 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1928 if (artwork.gfx_current == NULL)
1929 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1931 artwork.snd_current =
1932 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1933 if (artwork.snd_current == NULL)
1934 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1936 artwork.mus_current =
1937 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1938 if (artwork.mus_current == NULL)
1939 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1941 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1942 artwork.snd_current_identifier = artwork.snd_current->identifier;
1943 artwork.mus_current_identifier = artwork.mus_current->identifier;
1946 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1947 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1948 printf("music set == %s\n\n", artwork.mus_current_identifier);
1951 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1952 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1953 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1956 dumpTreeInfo(artwork.gfx_first, 0);
1957 dumpTreeInfo(artwork.snd_first, 0);
1958 dumpTreeInfo(artwork.mus_first, 0);
1962 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1963 LevelDirTree *level_node)
1965 /* recursively check all level directories for artwork sub-directories */
1969 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1970 ARTWORK_DIRECTORY((*artwork_node)->type));
1973 if (!level_node->parent_link)
1974 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1975 level_node->filename, level_node->name);
1978 if (!level_node->parent_link)
1980 TreeInfo *topnode_last = *artwork_node;
1982 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1983 (*artwork_node)->type);
1985 if (topnode_last != *artwork_node)
1987 free((*artwork_node)->identifier);
1988 free((*artwork_node)->name);
1989 free((*artwork_node)->name_sorting);
1991 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1992 (*artwork_node)->name = getStringCopy(level_node->name);
1993 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1995 (*artwork_node)->sort_priority = level_node->sort_priority;
1996 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2002 if (level_node->node_group != NULL)
2003 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2005 level_node = level_node->next;
2009 void LoadLevelArtworkInfo()
2011 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2013 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2014 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2015 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2017 /* needed for reloading level artwork not known at ealier stage */
2018 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2020 artwork.gfx_current =
2021 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2022 if (artwork.gfx_current == NULL)
2023 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2026 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2028 artwork.snd_current =
2029 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2030 if (artwork.snd_current == NULL)
2031 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2034 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2036 artwork.mus_current =
2037 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2038 if (artwork.mus_current == NULL)
2039 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2042 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2043 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2044 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2047 dumpTreeInfo(artwork.gfx_first, 0);
2048 dumpTreeInfo(artwork.snd_first, 0);
2049 dumpTreeInfo(artwork.mus_first, 0);
2053 static void SaveUserLevelInfo()
2059 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2061 if (!(file = fopen(filename, MODE_WRITE)))
2063 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2068 /* always start with reliable default values */
2069 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2071 ldi.name = getStringCopy(getLoginName());
2072 ldi.author = getStringCopy(getRealName());
2074 ldi.first_level = 1;
2075 ldi.sort_priority = LEVELCLASS_USER_START;
2076 ldi.readonly = FALSE;
2078 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2079 getCookie("LEVELINFO")));
2081 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2082 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2083 i != LEVELINFO_TOKEN_NAME_SORTING &&
2084 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2085 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2090 SetFilePermissions(filename, PERMS_PRIVATE);
2093 char *getSetupValue(int type, void *value)
2095 static char value_string[MAX_LINE_LEN];
2103 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2107 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2111 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2115 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2119 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2123 sprintf(value_string, "%d", *(int *)value);
2127 strcpy(value_string, *(char **)value);
2131 value_string[0] = '\0';
2135 return value_string;
2138 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2142 static char token_string[MAX_LINE_LEN];
2143 int token_type = token_info[token_nr].type;
2144 void *setup_value = token_info[token_nr].value;
2145 char *token_text = token_info[token_nr].text;
2146 char *value_string = getSetupValue(token_type, setup_value);
2148 /* build complete token string */
2149 sprintf(token_string, "%s%s", prefix, token_text);
2151 /* build setup entry line */
2152 line = getFormattedSetupEntry(token_string, value_string);
2154 if (token_type == TYPE_KEY_X11)
2156 Key key = *(Key *)setup_value;
2157 char *keyname = getKeyNameFromKey(key);
2159 /* add comment, if useful */
2160 if (strcmp(keyname, "(undefined)") != 0 &&
2161 strcmp(keyname, "(unknown)") != 0)
2163 /* add at least one whitespace */
2165 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2169 strcat(line, keyname);
2176 void LoadLevelSetup_LastSeries()
2179 struct SetupFileList *level_setup_list = NULL;
2181 /* always start with reliable default values */
2182 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2184 /* ----------------------------------------------------------------------- */
2185 /* ~/.<program>/levelsetup.conf */
2186 /* ----------------------------------------------------------------------- */
2188 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2190 if ((level_setup_list = loadSetupFileList(filename)))
2192 char *last_level_series =
2193 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2195 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2197 if (leveldir_current == NULL)
2198 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2200 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2202 freeSetupFileList(level_setup_list);
2205 Error(ERR_WARN, "using default setup values");
2210 void SaveLevelSetup_LastSeries()
2213 char *level_subdir = leveldir_current->filename;
2216 /* ----------------------------------------------------------------------- */
2217 /* ~/.<program>/levelsetup.conf */
2218 /* ----------------------------------------------------------------------- */
2220 InitUserDataDirectory();
2222 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2224 if (!(file = fopen(filename, MODE_WRITE)))
2226 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2231 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2232 getCookie("LEVELSETUP")));
2233 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2239 SetFilePermissions(filename, PERMS_PRIVATE);
2242 static void checkSeriesInfo()
2244 static char *level_directory = NULL;
2246 struct dirent *dir_entry;
2248 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2250 level_directory = getPath2((leveldir_current->user_defined ?
2251 getUserLevelDir(NULL) :
2252 options.level_directory),
2253 leveldir_current->fullpath);
2255 if ((dir = opendir(level_directory)) == NULL)
2257 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2261 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2263 if (strlen(dir_entry->d_name) > 4 &&
2264 dir_entry->d_name[3] == '.' &&
2265 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2267 char levelnum_str[4];
2270 strncpy(levelnum_str, dir_entry->d_name, 3);
2271 levelnum_str[3] = '\0';
2273 levelnum_value = atoi(levelnum_str);
2275 if (levelnum_value < leveldir_current->first_level)
2277 Error(ERR_WARN, "additional level %d found", levelnum_value);
2278 leveldir_current->first_level = levelnum_value;
2280 else if (levelnum_value > leveldir_current->last_level)
2282 Error(ERR_WARN, "additional level %d found", levelnum_value);
2283 leveldir_current->last_level = levelnum_value;
2291 void LoadLevelSetup_SeriesInfo()
2294 struct SetupFileList *level_setup_list = NULL;
2295 char *level_subdir = leveldir_current->filename;
2297 /* always start with reliable default values */
2298 level_nr = leveldir_current->first_level;
2300 checkSeriesInfo(leveldir_current);
2302 /* ----------------------------------------------------------------------- */
2303 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2304 /* ----------------------------------------------------------------------- */
2306 level_subdir = leveldir_current->filename;
2308 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2310 if ((level_setup_list = loadSetupFileList(filename)))
2314 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2318 level_nr = atoi(token_value);
2320 if (level_nr < leveldir_current->first_level)
2321 level_nr = leveldir_current->first_level;
2322 if (level_nr > leveldir_current->last_level)
2323 level_nr = leveldir_current->last_level;
2326 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2330 int level_nr = atoi(token_value);
2332 if (level_nr < leveldir_current->first_level)
2333 level_nr = leveldir_current->first_level;
2334 if (level_nr > leveldir_current->last_level + 1)
2335 level_nr = leveldir_current->last_level;
2337 if (leveldir_current->user_defined)
2338 level_nr = leveldir_current->last_level;
2340 leveldir_current->handicap_level = level_nr;
2343 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2345 freeSetupFileList(level_setup_list);
2348 Error(ERR_WARN, "using default setup values");
2353 void SaveLevelSetup_SeriesInfo()
2356 char *level_subdir = leveldir_current->filename;
2357 char *level_nr_str = int2str(level_nr, 0);
2358 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2361 /* ----------------------------------------------------------------------- */
2362 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2363 /* ----------------------------------------------------------------------- */
2365 InitLevelSetupDirectory(level_subdir);
2367 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2369 if (!(file = fopen(filename, MODE_WRITE)))
2371 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2376 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2377 getCookie("LEVELSETUP")));
2378 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2380 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2381 handicap_level_str));
2386 SetFilePermissions(filename, PERMS_PRIVATE);