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 char *getLevelArtworkDir(TreeInfo *ti)
314 char *artwork_path, *artwork_set;
316 if (ti == NULL || leveldir_current == NULL)
317 return NOT_AVAILABLE;
320 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
321 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
322 ti->type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path : NULL);
324 if (artwork_path != NULL)
328 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
329 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
330 ti->type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_set : NULL);
332 if (artwork_set != NULL)
334 TreeInfo *level_artwork = getTreeInfoFromIdentifier(ti, artwork_set);
336 if (level_artwork != NULL)
337 return getSetupArtworkDir(level_artwork);
340 return NOT_AVAILABLE;
343 char *getLevelFilename(int nr)
345 static char *filename = NULL;
346 char basename[MAX_FILENAME_LEN];
348 if (filename != NULL)
351 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
352 filename = getPath2(getCurrentLevelDir(), basename);
357 char *getTapeFilename(int nr)
359 static char *filename = NULL;
360 char basename[MAX_FILENAME_LEN];
362 if (filename != NULL)
365 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
366 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
371 char *getScoreFilename(int nr)
373 static char *filename = NULL;
374 char basename[MAX_FILENAME_LEN];
376 if (filename != NULL)
379 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
380 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
385 char *getSetupFilename()
387 static char *filename = NULL;
389 if (filename != NULL)
392 filename = getPath2(getSetupDir(), SETUP_FILENAME);
397 static char *getCorrectedImageBasename(char *basename)
399 char *result = basename;
401 #if defined(PLATFORM_MSDOS)
402 if (program.filename_prefix != NULL)
404 int prefix_len = strlen(program.filename_prefix);
406 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
407 result = &basename[prefix_len];
414 static boolean fileExists(char *filename)
417 printf("checking file '%s'\n", filename);
420 return (access(filename, F_OK) == 0);
423 char *getCustomImageFilename(char *basename)
425 static char *filename = NULL;
427 if (filename != NULL)
430 basename = getCorrectedImageBasename(basename);
432 if (!setup.override_level_graphics)
434 /* 1st try: look for special artwork configured in level series config */
435 filename = getPath2(getLevelArtworkDir(artwork.gfx_first), basename);
436 if (fileExists(filename))
439 /* 2nd try: look for special artwork in current level series directory */
440 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
441 if (fileExists(filename))
445 /* 3rd try: look for special artwork in configured artwork directory */
446 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
447 if (fileExists(filename))
450 /* 4th try: look for default artwork in new default artwork directory */
451 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
452 if (fileExists(filename))
455 /* 5th try: look for default artwork in old default artwork directory */
456 filename = getPath2(options.graphics_directory, basename);
457 if (fileExists(filename))
460 return NULL; /* cannot find specified artwork file anywhere */
463 char *getCustomSoundFilename(char *basename)
465 static char *filename = NULL;
467 if (filename != NULL)
470 if (!setup.override_level_sounds)
473 /* 1st try: look for special artwork configured in level series config */
474 filename = getPath2(getLevelArtworkDir(artwork.snd_first), basename);
475 if (fileExists(filename))
479 /* 2nd try: look for special artwork in current level series directory */
480 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
481 if (fileExists(filename))
485 /* 3rd try: look for special artwork in configured artwork directory */
486 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
487 if (fileExists(filename))
490 /* 4th try: look for default artwork in new default artwork directory */
491 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
492 if (fileExists(filename))
495 /* 5th try: look for default artwork in old default artwork directory */
496 filename = getPath2(options.sounds_directory, basename);
497 if (fileExists(filename))
500 return NULL; /* cannot find specified artwork file anywhere */
503 char *getCustomSoundConfigFilename()
505 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
508 char *getCustomMusicDirectory(void)
510 static char *directory = NULL;
512 if (directory != NULL)
515 if (!setup.override_level_music)
518 /* 1st try: look for special artwork configured in level series config */
519 directory = getStringCopy(getLevelArtworkDir(artwork.mus_first));
520 if (fileExists(directory))
524 /* 2nd try: look for special artwork in current level series directory */
525 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
526 if (fileExists(directory))
530 /* 3rd try: look for special artwork in configured artwork directory */
531 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
534 printf("DEBUG: checking directory '%s' ...\n", directory);
537 if (fileExists(directory))
540 /* 4th try: look for default artwork in new default artwork directory */
541 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
542 if (fileExists(directory))
545 /* 5th try: look for default artwork in old default artwork directory */
546 directory = getStringCopy(options.music_directory);
547 if (fileExists(directory))
550 return NULL; /* cannot find specified artwork file anywhere */
553 void InitTapeDirectory(char *level_subdir)
555 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
556 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
557 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
560 void InitScoreDirectory(char *level_subdir)
562 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
563 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
566 static void SaveUserLevelInfo();
568 void InitUserLevelDirectory(char *level_subdir)
570 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
572 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
573 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
574 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
580 void InitLevelSetupDirectory(char *level_subdir)
582 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
583 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
584 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
588 /* ------------------------------------------------------------------------- */
589 /* some functions to handle lists of level directories */
590 /* ------------------------------------------------------------------------- */
592 TreeInfo *newTreeInfo()
594 return checked_calloc(sizeof(TreeInfo));
597 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
599 node_new->next = *node_first;
600 *node_first = node_new;
603 int numTreeInfo(TreeInfo *node)
616 boolean validLevelSeries(TreeInfo *node)
618 return (node != NULL && !node->node_group && !node->parent_link);
621 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
626 if (node->node_group) /* enter level group (step down into tree) */
627 return getFirstValidTreeInfoEntry(node->node_group);
628 else if (node->parent_link) /* skip start entry of level group */
630 if (node->next) /* get first real level series entry */
631 return getFirstValidTreeInfoEntry(node->next);
632 else /* leave empty level group and go on */
633 return getFirstValidTreeInfoEntry(node->node_parent->next);
635 else /* this seems to be a regular level series */
639 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
644 if (node->node_parent == NULL) /* top level group */
645 return *node->node_top;
646 else /* sub level group */
647 return node->node_parent->node_group;
650 int numTreeInfoInGroup(TreeInfo *node)
652 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
655 int posTreeInfo(TreeInfo *node)
657 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
662 if (node_cmp == node)
666 node_cmp = node_cmp->next;
672 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
674 TreeInfo *node_default = node;
689 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
691 if (identifier == NULL)
696 if (node->node_group)
698 TreeInfo *node_group;
700 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
705 else if (!node->parent_link)
707 if (strcmp(identifier, node->identifier) == 0)
717 void dumpTreeInfo(TreeInfo *node, int depth)
721 printf("Dumping TreeInfo:\n");
725 for (i=0; i<(depth + 1) * 3; i++)
728 printf("filename == '%s' (%s) [%s] (%d)\n",
729 node->filename, node->name, node->identifier, node->sort_priority);
731 if (node->node_group != NULL)
732 dumpTreeInfo(node->node_group, depth + 1);
738 void sortTreeInfo(TreeInfo **node_first,
739 int (*compare_function)(const void *, const void *))
741 int num_nodes = numTreeInfo(*node_first);
742 TreeInfo **sort_array;
743 TreeInfo *node = *node_first;
749 /* allocate array for sorting structure pointers */
750 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
752 /* writing structure pointers to sorting array */
753 while (i < num_nodes && node) /* double boundary check... */
755 sort_array[i] = node;
761 /* sorting the structure pointers in the sorting array */
762 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
765 /* update the linkage of list elements with the sorted node array */
766 for (i=0; i<num_nodes - 1; i++)
767 sort_array[i]->next = sort_array[i + 1];
768 sort_array[num_nodes - 1]->next = NULL;
770 /* update the linkage of the main list anchor pointer */
771 *node_first = sort_array[0];
775 /* now recursively sort the level group structures */
779 if (node->node_group != NULL)
780 sortTreeInfo(&node->node_group, compare_function);
787 /* ========================================================================= */
788 /* some stuff from "files.c" */
789 /* ========================================================================= */
791 #if defined(PLATFORM_WIN32)
793 #define S_IRGRP S_IRUSR
796 #define S_IROTH S_IRUSR
799 #define S_IWGRP S_IWUSR
802 #define S_IWOTH S_IWUSR
805 #define S_IXGRP S_IXUSR
808 #define S_IXOTH S_IXUSR
811 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
816 #endif /* PLATFORM_WIN32 */
818 /* file permissions for newly written files */
819 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
820 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
821 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
823 #define MODE_W_PRIVATE (S_IWUSR)
824 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
825 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
827 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
828 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
830 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
831 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
833 char *getUserDataDir(void)
835 static char *userdata_dir = NULL;
839 char *home_dir = getHomeDir();
840 char *data_dir = program.userdata_directory;
842 userdata_dir = getPath2(home_dir, data_dir);
850 return getUserDataDir();
853 static mode_t posix_umask(mode_t mask)
855 #if defined(PLATFORM_UNIX)
862 static int posix_mkdir(const char *pathname, mode_t mode)
864 #if defined(PLATFORM_WIN32)
865 return mkdir(pathname);
867 return mkdir(pathname, mode);
871 void createDirectory(char *dir, char *text, int permission_class)
873 /* leave "other" permissions in umask untouched, but ensure group parts
874 of USERDATA_DIR_MODE are not masked */
875 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
876 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
877 mode_t normal_umask = posix_umask(0);
878 mode_t group_umask = ~(dir_mode & S_IRWXG);
879 posix_umask(normal_umask & group_umask);
881 if (access(dir, F_OK) != 0)
882 if (posix_mkdir(dir, dir_mode) != 0)
883 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
885 posix_umask(normal_umask); /* reset normal umask */
888 void InitUserDataDirectory()
890 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
893 void SetFilePermissions(char *filename, int permission_class)
895 chmod(filename, (permission_class == PERMS_PRIVATE ?
896 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
899 char *getCookie(char *file_type)
901 static char cookie[MAX_COOKIE_LEN + 1];
903 if (strlen(program.cookie_prefix) + 1 +
904 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
905 return "[COOKIE ERROR]"; /* should never happen */
907 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
908 program.cookie_prefix, file_type,
909 program.version_major, program.version_minor);
914 int getFileVersionFromCookieString(const char *cookie)
916 const char *ptr_cookie1, *ptr_cookie2;
917 const char *pattern1 = "_FILE_VERSION_";
918 const char *pattern2 = "?.?";
919 const int len_cookie = strlen(cookie);
920 const int len_pattern1 = strlen(pattern1);
921 const int len_pattern2 = strlen(pattern2);
922 const int len_pattern = len_pattern1 + len_pattern2;
923 int version_major, version_minor;
925 if (len_cookie <= len_pattern)
928 ptr_cookie1 = &cookie[len_cookie - len_pattern];
929 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
931 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
934 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
935 ptr_cookie2[1] != '.' ||
936 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
939 version_major = ptr_cookie2[0] - '0';
940 version_minor = ptr_cookie2[2] - '0';
942 return VERSION_IDENT(version_major, version_minor, 0);
945 boolean checkCookieString(const char *cookie, const char *template)
947 const char *pattern = "_FILE_VERSION_?.?";
948 const int len_cookie = strlen(cookie);
949 const int len_template = strlen(template);
950 const int len_pattern = strlen(pattern);
952 if (len_cookie != len_template)
955 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
961 /* ------------------------------------------------------------------------- */
962 /* setup file list handling functions */
963 /* ------------------------------------------------------------------------- */
965 int get_string_integer_value(char *s)
967 static char *number_text[][3] =
969 { "0", "zero", "null", },
970 { "1", "one", "first" },
971 { "2", "two", "second" },
972 { "3", "three", "third" },
973 { "4", "four", "fourth" },
974 { "5", "five", "fifth" },
975 { "6", "six", "sixth" },
976 { "7", "seven", "seventh" },
977 { "8", "eight", "eighth" },
978 { "9", "nine", "ninth" },
979 { "10", "ten", "tenth" },
980 { "11", "eleven", "eleventh" },
981 { "12", "twelve", "twelfth" },
985 char *s_lower = getStringToLower(s);
990 if (strcmp(s_lower, number_text[i][j]) == 0)
1001 boolean get_string_boolean_value(char *s)
1003 char *s_lower = getStringToLower(s);
1004 boolean result = FALSE;
1006 if (strcmp(s_lower, "true") == 0 ||
1007 strcmp(s_lower, "yes") == 0 ||
1008 strcmp(s_lower, "on") == 0 ||
1009 get_string_integer_value(s) == 1)
1017 char *getFormattedSetupEntry(char *token, char *value)
1020 static char entry[MAX_LINE_LEN];
1022 /* start with the token and some spaces to format output line */
1023 sprintf(entry, "%s:", token);
1024 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1027 /* continue with the token's value */
1028 strcat(entry, value);
1033 void freeSetupFileList(struct SetupFileList *setup_file_list)
1035 if (!setup_file_list)
1038 if (setup_file_list->token)
1039 free(setup_file_list->token);
1040 if (setup_file_list->value)
1041 free(setup_file_list->value);
1042 if (setup_file_list->next)
1043 freeSetupFileList(setup_file_list->next);
1044 free(setup_file_list);
1047 static struct SetupFileList *newSetupFileList(char *token, char *value)
1049 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1051 new->token = checked_malloc(strlen(token) + 1);
1052 strcpy(new->token, token);
1054 new->value = checked_malloc(strlen(value) + 1);
1055 strcpy(new->value, value);
1062 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1064 if (!setup_file_list)
1067 if (strcmp(setup_file_list->token, token) == 0)
1068 return setup_file_list->value;
1070 return getTokenValue(setup_file_list->next, token);
1073 static void setTokenValue(struct SetupFileList *setup_file_list,
1074 char *token, char *value)
1076 if (!setup_file_list)
1079 if (strcmp(setup_file_list->token, token) == 0)
1081 free(setup_file_list->value);
1082 setup_file_list->value = checked_malloc(strlen(value) + 1);
1083 strcpy(setup_file_list->value, value);
1085 else if (setup_file_list->next == NULL)
1086 setup_file_list->next = newSetupFileList(token, value);
1088 setTokenValue(setup_file_list->next, token, value);
1092 static void printSetupFileList(struct SetupFileList *setup_file_list)
1094 if (!setup_file_list)
1097 printf("token: '%s'\n", setup_file_list->token);
1098 printf("value: '%s'\n", setup_file_list->value);
1100 printSetupFileList(setup_file_list->next);
1104 struct SetupFileList *loadSetupFileList(char *filename)
1107 char line[MAX_LINE_LEN];
1108 char *token, *value, *line_ptr;
1109 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1110 struct SetupFileList *first_valid_list_entry;
1114 if (!(file = fopen(filename, MODE_READ)))
1116 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1122 /* read next line of input file */
1123 if (!fgets(line, MAX_LINE_LEN, file))
1126 /* cut trailing comment or whitespace from input line */
1127 for (line_ptr = line; *line_ptr; line_ptr++)
1129 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1136 /* cut trailing whitespaces from input line */
1137 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1138 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1141 /* ignore empty lines */
1145 line_len = strlen(line);
1147 /* cut leading whitespaces from token */
1148 for (token = line; *token; token++)
1149 if (*token != ' ' && *token != '\t')
1152 /* find end of token */
1153 for (line_ptr = token; *line_ptr; line_ptr++)
1155 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1162 if (line_ptr < line + line_len)
1163 value = line_ptr + 1;
1167 /* cut leading whitespaces from value */
1168 for (; *value; value++)
1169 if (*value != ' ' && *value != '\t')
1172 if (*token && *value)
1173 setTokenValue(setup_file_list, token, value);
1178 first_valid_list_entry = setup_file_list->next;
1180 /* free empty list header */
1181 setup_file_list->next = NULL;
1182 freeSetupFileList(setup_file_list);
1184 if (first_valid_list_entry == NULL)
1185 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1187 return first_valid_list_entry;
1190 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1193 if (!setup_file_list)
1196 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1198 if (!checkCookieString(setup_file_list->value, identifier))
1200 Error(ERR_WARN, "configuration file has wrong file identifier");
1207 if (setup_file_list->next)
1208 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1211 Error(ERR_WARN, "configuration file has no file identifier");
1217 /* ========================================================================= */
1218 /* setup file stuff */
1219 /* ========================================================================= */
1221 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1222 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1223 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1225 /* level directory info */
1226 #define LEVELINFO_TOKEN_IDENTIFIER 0
1227 #define LEVELINFO_TOKEN_NAME 1
1228 #define LEVELINFO_TOKEN_NAME_SORTING 2
1229 #define LEVELINFO_TOKEN_AUTHOR 3
1230 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1231 #define LEVELINFO_TOKEN_LEVELS 5
1232 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1233 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1234 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1235 #define LEVELINFO_TOKEN_READONLY 9
1236 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1237 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1238 #define LEVELINFO_TOKEN_MUSIC_SET 12
1240 #define NUM_LEVELINFO_TOKENS 13
1242 static LevelDirTree ldi;
1244 static struct TokenInfo levelinfo_tokens[] =
1246 /* level directory info */
1247 { TYPE_STRING, &ldi.identifier, "identifier" },
1248 { TYPE_STRING, &ldi.name, "name" },
1249 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1250 { TYPE_STRING, &ldi.author, "author" },
1251 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1252 { TYPE_INTEGER, &ldi.levels, "levels" },
1253 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1254 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1255 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1256 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1257 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1258 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1259 { TYPE_STRING, &ldi.music_set, "music_set" }
1262 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1266 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1267 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1268 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1269 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1272 ldi->node_parent = NULL;
1273 ldi->node_group = NULL;
1277 ldi->cl_cursor = -1;
1279 ldi->filename = NULL;
1280 ldi->fullpath = NULL;
1281 ldi->basepath = NULL;
1282 ldi->identifier = NULL;
1283 ldi->name = getStringCopy(ANONYMOUS_NAME);
1284 ldi->name_sorting = NULL;
1285 ldi->author = getStringCopy(ANONYMOUS_NAME);
1287 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1288 ldi->parent_link = FALSE;
1289 ldi->user_defined = FALSE;
1291 ldi->class_desc = NULL;
1293 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1295 ldi->imported_from = NULL;
1296 ldi->graphics_set = NULL;
1297 ldi->sounds_set = NULL;
1298 ldi->music_set = NULL;
1299 ldi->graphics_path = getStringCopy(NOT_AVAILABLE);
1300 ldi->sounds_path = getStringCopy(NOT_AVAILABLE);
1301 ldi->music_path = getStringCopy(NOT_AVAILABLE);
1303 ldi->first_level = 0;
1304 ldi->last_level = 0;
1305 ldi->level_group = FALSE;
1306 ldi->handicap_level = 0;
1307 ldi->readonly = TRUE;
1311 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1315 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1317 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1321 /* first copy all values from the parent structure ... */
1324 /* ... then set all fields to default that cannot be inherited from parent.
1325 This is especially important for all those fields that can be set from
1326 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1327 calls 'free()' for all already set token values which requires that no
1328 other structure's pointer may point to them!
1331 ldi->filename = NULL;
1332 ldi->fullpath = NULL;
1333 ldi->basepath = NULL;
1334 ldi->identifier = NULL;
1335 ldi->name = getStringCopy(ANONYMOUS_NAME);
1336 ldi->name_sorting = NULL;
1337 ldi->author = getStringCopy(parent->author);
1338 ldi->imported_from = getStringCopy(parent->imported_from);
1340 ldi->level_group = FALSE;
1341 ldi->parent_link = FALSE;
1343 ldi->node_top = parent->node_top;
1344 ldi->node_parent = parent;
1345 ldi->node_group = NULL;
1349 void setSetupInfo(struct TokenInfo *token_info,
1350 int token_nr, char *token_value)
1352 int token_type = token_info[token_nr].type;
1353 void *setup_value = token_info[token_nr].value;
1355 if (token_value == NULL)
1358 /* set setup field to corresponding token value */
1363 *(boolean *)setup_value = get_string_boolean_value(token_value);
1367 *(Key *)setup_value = getKeyFromKeyName(token_value);
1371 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1375 *(int *)setup_value = get_string_integer_value(token_value);
1379 if (*(char **)setup_value != NULL)
1380 free(*(char **)setup_value);
1381 *(char **)setup_value = getStringCopy(token_value);
1389 static int compareTreeInfoEntries(const void *object1, const void *object2)
1391 const TreeInfo *entry1 = *((TreeInfo **)object1);
1392 const TreeInfo *entry2 = *((TreeInfo **)object2);
1393 int class_sorting1, class_sorting2;
1396 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1398 class_sorting1 = LEVELSORTING(entry1);
1399 class_sorting2 = LEVELSORTING(entry2);
1403 class_sorting1 = ARTWORKSORTING(entry1);
1404 class_sorting2 = ARTWORKSORTING(entry2);
1407 if (entry1->parent_link || entry2->parent_link)
1408 compare_result = (entry1->parent_link ? -1 : +1);
1409 else if (entry1->sort_priority == entry2->sort_priority)
1411 char *name1 = getStringToLower(entry1->name_sorting);
1412 char *name2 = getStringToLower(entry2->name_sorting);
1414 compare_result = strcmp(name1, name2);
1419 else if (class_sorting1 == class_sorting2)
1420 compare_result = entry1->sort_priority - entry2->sort_priority;
1422 compare_result = class_sorting1 - class_sorting2;
1424 return compare_result;
1427 static void createParentTreeInfoNode(TreeInfo *node_parent)
1431 if (node_parent == NULL)
1434 ti_new = newTreeInfo();
1435 setTreeInfoToDefaults(ti_new, node_parent->type);
1437 ti_new->node_parent = node_parent;
1438 ti_new->parent_link = TRUE;
1440 ti_new->identifier = getStringCopy(node_parent->identifier);
1441 ti_new->name = ".. (parent directory)";
1442 ti_new->name_sorting = getStringCopy(ti_new->name);
1444 ti_new->filename = "..";
1445 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1447 ti_new->sort_priority = node_parent->sort_priority;
1448 ti_new->class_desc = getLevelClassDescription(ti_new);
1450 pushTreeInfo(&node_parent->node_group, ti_new);
1453 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1454 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1456 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1457 TreeInfo *node_parent,
1458 char *level_directory,
1459 char *directory_name)
1461 char *directory_path = getPath2(level_directory, directory_name);
1462 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1463 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1464 LevelDirTree *leveldir_new = NULL;
1467 if (setup_file_list == NULL)
1469 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1471 free(directory_path);
1477 leveldir_new = newTreeInfo();
1480 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1482 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1484 leveldir_new->filename = getStringCopy(directory_name);
1486 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1488 /* set all structure fields according to the token/value pairs */
1489 ldi = *leveldir_new;
1490 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1491 setSetupInfo(levelinfo_tokens, i,
1492 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1493 *leveldir_new = ldi;
1495 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1497 free(leveldir_new->name);
1498 leveldir_new->name = getStringCopy(leveldir_new->filename);
1501 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1503 if (leveldir_new->identifier == NULL)
1504 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1506 if (leveldir_new->name_sorting == NULL)
1507 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1509 if (node_parent == NULL) /* top level group */
1511 leveldir_new->basepath = level_directory;
1512 leveldir_new->fullpath = leveldir_new->filename;
1514 else /* sub level group */
1516 leveldir_new->basepath = node_parent->basepath;
1517 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1520 if (leveldir_new->levels < 1)
1521 leveldir_new->levels = 1;
1523 leveldir_new->last_level =
1524 leveldir_new->first_level + leveldir_new->levels - 1;
1526 leveldir_new->user_defined =
1527 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1529 leveldir_new->color = LEVELCOLOR(leveldir_new);
1530 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1532 leveldir_new->handicap_level = /* set handicap to default value */
1533 (leveldir_new->user_defined ?
1534 leveldir_new->last_level :
1535 leveldir_new->first_level);
1537 pushTreeInfo(node_first, leveldir_new);
1539 freeSetupFileList(setup_file_list);
1541 if (leveldir_new->level_group)
1543 /* create node to link back to current level directory */
1544 createParentTreeInfoNode(leveldir_new);
1546 /* step into sub-directory and look for more level series */
1547 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1548 leveldir_new, directory_path);
1551 free(directory_path);
1557 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1558 TreeInfo *node_parent,
1559 char *level_directory)
1562 struct dirent *dir_entry;
1563 boolean valid_entry_found = FALSE;
1565 if ((dir = opendir(level_directory)) == NULL)
1567 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1571 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1573 struct stat file_status;
1574 char *directory_name = dir_entry->d_name;
1575 char *directory_path = getPath2(level_directory, directory_name);
1577 /* skip entries for current and parent directory */
1578 if (strcmp(directory_name, ".") == 0 ||
1579 strcmp(directory_name, "..") == 0)
1581 free(directory_path);
1585 /* find out if directory entry is itself a directory */
1586 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1587 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1589 free(directory_path);
1593 free(directory_path);
1595 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1596 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1597 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1600 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1607 if (!valid_entry_found)
1609 /* check if this directory directly contains a file "levelinfo.conf" */
1610 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1611 level_directory, ".");
1614 if (!valid_entry_found)
1615 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1619 void LoadLevelInfo()
1621 InitUserLevelDirectory(getLoginName());
1623 DrawInitText("Loading level series:", 120, FC_GREEN);
1625 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1626 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1628 /* before sorting, the first entries will be from the user directory */
1629 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1631 if (leveldir_first == NULL)
1632 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1634 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1637 dumpTreeInfo(leveldir_first, 0);
1641 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1642 TreeInfo *node_parent,
1643 char *base_directory,
1644 char *directory_name, int type)
1646 char *directory_path = getPath2(base_directory, directory_name);
1647 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1648 struct SetupFileList *setup_file_list = NULL;
1649 TreeInfo *artwork_new = NULL;
1652 if (access(filename, F_OK) == 0) /* file exists */
1653 setup_file_list = loadSetupFileList(filename);
1655 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1658 struct dirent *dir_entry;
1659 boolean valid_file_found = FALSE;
1661 if ((dir = opendir(directory_path)) != NULL)
1663 while ((dir_entry = readdir(dir)) != NULL)
1665 char *entry_name = dir_entry->d_name;
1667 if (FileIsArtworkType(entry_name, type))
1669 valid_file_found = TRUE;
1677 if (!valid_file_found)
1679 if (strcmp(directory_name, ".") != 0)
1680 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1682 free(directory_path);
1689 artwork_new = newTreeInfo();
1692 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1694 setTreeInfoToDefaults(artwork_new, type);
1696 artwork_new->filename = getStringCopy(directory_name);
1698 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1701 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1704 /* set all structure fields according to the token/value pairs */
1706 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1707 setSetupInfo(levelinfo_tokens, i,
1708 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1711 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1713 free(artwork_new->name);
1714 artwork_new->name = getStringCopy(artwork_new->filename);
1718 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1721 if (artwork_new->identifier == NULL)
1722 artwork_new->identifier = getStringCopy(artwork_new->filename);
1724 if (artwork_new->name_sorting == NULL)
1725 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1728 if (node_parent == NULL) /* top level group */
1730 artwork_new->basepath = getStringCopy(base_directory);
1731 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1733 else /* sub level group */
1735 artwork_new->basepath = getStringCopy(node_parent->basepath);
1736 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1739 artwork_new->user_defined =
1740 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1742 /* (may use ".sort_priority" from "setup_file_list" above) */
1743 artwork_new->color = ARTWORKCOLOR(artwork_new);
1744 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1746 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1748 if (artwork_new->name != NULL)
1749 free(artwork_new->name);
1751 if (strcmp(artwork_new->filename, ".") == 0)
1753 if (artwork_new->user_defined)
1755 artwork_new->identifier = getStringCopy("private");
1756 artwork_new->sort_priority = ARTWORKCLASS_USER;
1760 artwork_new->identifier = getStringCopy("classic");
1761 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1764 /* set to new values after changing ".sort_priority" */
1765 artwork_new->color = ARTWORKCOLOR(artwork_new);
1766 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1770 artwork_new->identifier = getStringCopy(artwork_new->filename);
1773 artwork_new->name = getStringCopy(artwork_new->identifier);
1774 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1777 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1779 pushTreeInfo(node_first, artwork_new);
1781 freeSetupFileList(setup_file_list);
1783 free(directory_path);
1789 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1790 TreeInfo *node_parent,
1791 char *base_directory, int type)
1794 struct dirent *dir_entry;
1795 boolean valid_entry_found = FALSE;
1797 if ((dir = opendir(base_directory)) == NULL)
1799 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1800 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1804 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1806 struct stat file_status;
1807 char *directory_name = dir_entry->d_name;
1808 char *directory_path = getPath2(base_directory, directory_name);
1810 /* skip entries for current and parent directory */
1811 if (strcmp(directory_name, ".") == 0 ||
1812 strcmp(directory_name, "..") == 0)
1814 free(directory_path);
1818 /* find out if directory entry is itself a directory */
1819 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1820 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1822 free(directory_path);
1826 free(directory_path);
1828 /* check if this directory contains artwork with or without config file */
1829 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1831 directory_name, type);
1836 /* check if this directory directly contains artwork itself */
1837 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1838 base_directory, ".",
1840 if (!valid_entry_found)
1841 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1845 static TreeInfo *getDummyArtworkInfo(int type)
1847 /* this is only needed when there is completely no artwork available */
1848 TreeInfo *artwork_new = newTreeInfo();
1850 setTreeInfoToDefaults(artwork_new, type);
1852 artwork_new->filename = getStringCopy(NOT_AVAILABLE);
1853 artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
1854 artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
1856 if (artwork_new->name != NULL)
1857 free(artwork_new->name);
1859 artwork_new->identifier = getStringCopy(NOT_AVAILABLE);
1860 artwork_new->name = getStringCopy(NOT_AVAILABLE);
1861 artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
1866 void LoadArtworkInfo()
1868 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1870 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1871 options.graphics_directory,
1872 TREE_TYPE_GRAPHICS_DIR);
1873 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1874 getUserGraphicsDir(),
1875 TREE_TYPE_GRAPHICS_DIR);
1877 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1878 options.sounds_directory,
1879 TREE_TYPE_SOUNDS_DIR);
1880 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1882 TREE_TYPE_SOUNDS_DIR);
1884 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1885 options.music_directory,
1886 TREE_TYPE_MUSIC_DIR);
1887 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1889 TREE_TYPE_MUSIC_DIR);
1891 if (artwork.gfx_first == NULL)
1892 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1893 if (artwork.snd_first == NULL)
1894 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1895 if (artwork.mus_first == NULL)
1896 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1898 /* before sorting, the first entries will be from the user directory */
1899 artwork.gfx_current =
1900 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1901 if (artwork.gfx_current == NULL)
1902 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1904 artwork.snd_current =
1905 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1906 if (artwork.snd_current == NULL)
1907 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1909 artwork.mus_current =
1910 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1911 if (artwork.mus_current == NULL)
1912 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1914 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1915 artwork.snd_current_identifier = artwork.snd_current->identifier;
1916 artwork.mus_current_identifier = artwork.mus_current->identifier;
1919 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1920 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1921 printf("music set == %s\n\n", artwork.mus_current_identifier);
1924 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1925 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1926 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1929 dumpTreeInfo(artwork.gfx_first, 0);
1930 dumpTreeInfo(artwork.snd_first, 0);
1931 dumpTreeInfo(artwork.mus_first, 0);
1935 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1936 LevelDirTree *level_node)
1938 /* recursively check all level directories for artwork sub-directories */
1942 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1943 ARTWORK_DIRECTORY((*artwork_node)->type));
1946 if (!level_node->parent_link)
1947 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1948 level_node->filename, level_node->name);
1951 if (!level_node->parent_link)
1953 TreeInfo *topnode_last = *artwork_node;
1955 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1956 (*artwork_node)->type);
1958 if (topnode_last != *artwork_node)
1960 free((*artwork_node)->identifier);
1961 free((*artwork_node)->name);
1962 free((*artwork_node)->name_sorting);
1964 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1965 (*artwork_node)->name = getStringCopy(level_node->name);
1966 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1968 (*artwork_node)->sort_priority = level_node->sort_priority;
1969 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1975 if (level_node->node_group != NULL)
1976 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1978 level_node = level_node->next;
1982 void LoadLevelArtworkInfo()
1984 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1986 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1987 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1988 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1990 /* needed for reloading level artwork not known at ealier stage */
1991 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
1993 artwork.gfx_current =
1994 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1995 if (artwork.gfx_current == NULL)
1996 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1999 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2001 artwork.snd_current =
2002 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2003 if (artwork.snd_current == NULL)
2004 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2007 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2009 artwork.mus_current =
2010 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2011 if (artwork.mus_current == NULL)
2012 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2015 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2016 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2017 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2020 dumpTreeInfo(artwork.gfx_first, 0);
2021 dumpTreeInfo(artwork.snd_first, 0);
2022 dumpTreeInfo(artwork.mus_first, 0);
2026 static void SaveUserLevelInfo()
2032 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2034 if (!(file = fopen(filename, MODE_WRITE)))
2036 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2041 /* always start with reliable default values */
2042 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2044 ldi.name = getStringCopy(getLoginName());
2045 ldi.author = getStringCopy(getRealName());
2047 ldi.first_level = 1;
2048 ldi.sort_priority = LEVELCLASS_USER_START;
2049 ldi.readonly = FALSE;
2051 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2052 getCookie("LEVELINFO")));
2054 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2055 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2056 i != LEVELINFO_TOKEN_NAME_SORTING &&
2057 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2058 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2063 SetFilePermissions(filename, PERMS_PRIVATE);
2066 char *getSetupValue(int type, void *value)
2068 static char value_string[MAX_LINE_LEN];
2076 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2080 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2084 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2088 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2092 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2096 sprintf(value_string, "%d", *(int *)value);
2100 strcpy(value_string, *(char **)value);
2104 value_string[0] = '\0';
2108 return value_string;
2111 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2115 static char token_string[MAX_LINE_LEN];
2116 int token_type = token_info[token_nr].type;
2117 void *setup_value = token_info[token_nr].value;
2118 char *token_text = token_info[token_nr].text;
2119 char *value_string = getSetupValue(token_type, setup_value);
2121 /* build complete token string */
2122 sprintf(token_string, "%s%s", prefix, token_text);
2124 /* build setup entry line */
2125 line = getFormattedSetupEntry(token_string, value_string);
2127 if (token_type == TYPE_KEY_X11)
2129 Key key = *(Key *)setup_value;
2130 char *keyname = getKeyNameFromKey(key);
2132 /* add comment, if useful */
2133 if (strcmp(keyname, "(undefined)") != 0 &&
2134 strcmp(keyname, "(unknown)") != 0)
2136 /* add at least one whitespace */
2138 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2142 strcat(line, keyname);
2149 void LoadLevelSetup_LastSeries()
2152 struct SetupFileList *level_setup_list = NULL;
2154 /* always start with reliable default values */
2155 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2157 /* ----------------------------------------------------------------------- */
2158 /* ~/.<program>/levelsetup.conf */
2159 /* ----------------------------------------------------------------------- */
2161 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2163 if ((level_setup_list = loadSetupFileList(filename)))
2165 char *last_level_series =
2166 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2168 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2170 if (leveldir_current == NULL)
2171 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2173 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2175 freeSetupFileList(level_setup_list);
2178 Error(ERR_WARN, "using default setup values");
2183 void SaveLevelSetup_LastSeries()
2186 char *level_subdir = leveldir_current->filename;
2189 /* ----------------------------------------------------------------------- */
2190 /* ~/.<program>/levelsetup.conf */
2191 /* ----------------------------------------------------------------------- */
2193 InitUserDataDirectory();
2195 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2197 if (!(file = fopen(filename, MODE_WRITE)))
2199 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2204 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2205 getCookie("LEVELSETUP")));
2206 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2212 SetFilePermissions(filename, PERMS_PRIVATE);
2215 static void checkSeriesInfo()
2217 static char *level_directory = NULL;
2219 struct dirent *dir_entry;
2221 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2223 level_directory = getPath2((leveldir_current->user_defined ?
2224 getUserLevelDir(NULL) :
2225 options.level_directory),
2226 leveldir_current->fullpath);
2228 if ((dir = opendir(level_directory)) == NULL)
2230 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2234 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2236 if (strlen(dir_entry->d_name) > 4 &&
2237 dir_entry->d_name[3] == '.' &&
2238 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2240 char levelnum_str[4];
2243 strncpy(levelnum_str, dir_entry->d_name, 3);
2244 levelnum_str[3] = '\0';
2246 levelnum_value = atoi(levelnum_str);
2248 if (levelnum_value < leveldir_current->first_level)
2250 Error(ERR_WARN, "additional level %d found", levelnum_value);
2251 leveldir_current->first_level = levelnum_value;
2253 else if (levelnum_value > leveldir_current->last_level)
2255 Error(ERR_WARN, "additional level %d found", levelnum_value);
2256 leveldir_current->last_level = levelnum_value;
2264 void LoadLevelSetup_SeriesInfo()
2267 struct SetupFileList *level_setup_list = NULL;
2268 char *level_subdir = leveldir_current->filename;
2270 /* always start with reliable default values */
2271 level_nr = leveldir_current->first_level;
2273 checkSeriesInfo(leveldir_current);
2275 /* ----------------------------------------------------------------------- */
2276 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2277 /* ----------------------------------------------------------------------- */
2279 level_subdir = leveldir_current->filename;
2281 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2283 if ((level_setup_list = loadSetupFileList(filename)))
2287 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2291 level_nr = atoi(token_value);
2293 if (level_nr < leveldir_current->first_level)
2294 level_nr = leveldir_current->first_level;
2295 if (level_nr > leveldir_current->last_level)
2296 level_nr = leveldir_current->last_level;
2299 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2303 int level_nr = atoi(token_value);
2305 if (level_nr < leveldir_current->first_level)
2306 level_nr = leveldir_current->first_level;
2307 if (level_nr > leveldir_current->last_level + 1)
2308 level_nr = leveldir_current->last_level;
2310 if (leveldir_current->user_defined)
2311 level_nr = leveldir_current->last_level;
2313 leveldir_current->handicap_level = level_nr;
2316 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2318 freeSetupFileList(level_setup_list);
2321 Error(ERR_WARN, "using default setup values");
2326 void SaveLevelSetup_SeriesInfo()
2329 char *level_subdir = leveldir_current->filename;
2330 char *level_nr_str = int2str(level_nr, 0);
2331 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2334 /* ----------------------------------------------------------------------- */
2335 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2336 /* ----------------------------------------------------------------------- */
2338 InitLevelSetupDirectory(level_subdir);
2340 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2342 if (!(file = fopen(filename, MODE_WRITE)))
2344 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2349 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2350 getCookie("LEVELSETUP")));
2351 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2353 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2354 handicap_level_str));
2359 SetFilePermissions(filename, PERMS_PRIVATE);