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 NOT_AVAILABLE;
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 *result = 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 result = &basename[prefix_len];
426 static boolean fileExists(char *filename)
429 printf("checking file '%s'\n", filename);
432 return (access(filename, F_OK) == 0);
435 char *getCustomImageFilename(char *basename)
437 static char *filename = NULL;
439 if (filename != NULL)
442 basename = getCorrectedImageBasename(basename);
444 if (!setup.override_level_graphics)
446 /* 1st try: look for special artwork configured in level series config */
447 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
448 if (fileExists(filename))
451 /* 2nd try: look for special artwork in current level series directory */
452 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
453 if (fileExists(filename))
457 /* 3rd try: look for special artwork in configured artwork directory */
458 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
459 if (fileExists(filename))
462 /* 4th try: look for default artwork in new default artwork directory */
463 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
464 if (fileExists(filename))
467 /* 5th try: look for default artwork in old default artwork directory */
468 filename = getPath2(options.graphics_directory, basename);
469 if (fileExists(filename))
472 return NULL; /* cannot find specified artwork file anywhere */
475 char *getCustomSoundFilename(char *basename)
477 static char *filename = NULL;
479 if (filename != NULL)
482 if (!setup.override_level_sounds)
484 /* 1st try: look for special artwork configured in level series config */
485 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
486 if (fileExists(filename))
489 /* 2nd try: look for special artwork in current level series directory */
490 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
491 if (fileExists(filename))
495 /* 3rd try: look for special artwork in configured artwork directory */
496 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
497 if (fileExists(filename))
500 /* 4th try: look for default artwork in new default artwork directory */
501 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
502 if (fileExists(filename))
505 /* 5th try: look for default artwork in old default artwork directory */
506 filename = getPath2(options.sounds_directory, basename);
507 if (fileExists(filename))
510 return NULL; /* cannot find specified artwork file anywhere */
513 char *getCustomSoundConfigFilename()
515 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
518 char *getCustomMusicDirectory(void)
520 static char *directory = NULL;
522 if (directory != NULL)
525 if (!setup.override_level_music)
527 /* 1st try: look for special artwork configured in level series config */
528 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
529 if (fileExists(directory))
532 /* 2nd try: look for special artwork in current level series directory */
533 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
534 if (fileExists(directory))
538 /* 3rd try: look for special artwork in configured artwork directory */
539 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
540 if (fileExists(directory))
543 /* 4th try: look for default artwork in new default artwork directory */
544 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
545 if (fileExists(directory))
548 /* 5th try: look for default artwork in old default artwork directory */
549 directory = getStringCopy(options.music_directory);
550 if (fileExists(directory))
553 return NULL; /* cannot find specified artwork file anywhere */
556 void InitTapeDirectory(char *level_subdir)
558 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
559 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
560 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
563 void InitScoreDirectory(char *level_subdir)
565 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
566 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
569 static void SaveUserLevelInfo();
571 void InitUserLevelDirectory(char *level_subdir)
573 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
575 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
576 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
577 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
583 void InitLevelSetupDirectory(char *level_subdir)
585 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
586 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
587 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
591 /* ------------------------------------------------------------------------- */
592 /* some functions to handle lists of level directories */
593 /* ------------------------------------------------------------------------- */
595 TreeInfo *newTreeInfo()
597 return checked_calloc(sizeof(TreeInfo));
600 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
602 node_new->next = *node_first;
603 *node_first = node_new;
606 int numTreeInfo(TreeInfo *node)
619 boolean validLevelSeries(TreeInfo *node)
621 return (node != NULL && !node->node_group && !node->parent_link);
624 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
629 if (node->node_group) /* enter level group (step down into tree) */
630 return getFirstValidTreeInfoEntry(node->node_group);
631 else if (node->parent_link) /* skip start entry of level group */
633 if (node->next) /* get first real level series entry */
634 return getFirstValidTreeInfoEntry(node->next);
635 else /* leave empty level group and go on */
636 return getFirstValidTreeInfoEntry(node->node_parent->next);
638 else /* this seems to be a regular level series */
642 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
647 if (node->node_parent == NULL) /* top level group */
648 return *node->node_top;
649 else /* sub level group */
650 return node->node_parent->node_group;
653 int numTreeInfoInGroup(TreeInfo *node)
655 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
658 int posTreeInfo(TreeInfo *node)
660 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
665 if (node_cmp == node)
669 node_cmp = node_cmp->next;
675 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
677 TreeInfo *node_default = node;
692 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
694 if (identifier == NULL)
699 if (node->node_group)
701 TreeInfo *node_group;
703 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
708 else if (!node->parent_link)
710 if (strcmp(identifier, node->identifier) == 0)
720 void dumpTreeInfo(TreeInfo *node, int depth)
724 printf("Dumping TreeInfo:\n");
728 for (i=0; i<(depth + 1) * 3; i++)
731 printf("filename == '%s' (%s) [%s] (%d)\n",
732 node->filename, node->name, node->identifier, node->sort_priority);
734 if (node->node_group != NULL)
735 dumpTreeInfo(node->node_group, depth + 1);
741 void sortTreeInfo(TreeInfo **node_first,
742 int (*compare_function)(const void *, const void *))
744 int num_nodes = numTreeInfo(*node_first);
745 TreeInfo **sort_array;
746 TreeInfo *node = *node_first;
752 /* allocate array for sorting structure pointers */
753 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
755 /* writing structure pointers to sorting array */
756 while (i < num_nodes && node) /* double boundary check... */
758 sort_array[i] = node;
764 /* sorting the structure pointers in the sorting array */
765 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
768 /* update the linkage of list elements with the sorted node array */
769 for (i=0; i<num_nodes - 1; i++)
770 sort_array[i]->next = sort_array[i + 1];
771 sort_array[num_nodes - 1]->next = NULL;
773 /* update the linkage of the main list anchor pointer */
774 *node_first = sort_array[0];
778 /* now recursively sort the level group structures */
782 if (node->node_group != NULL)
783 sortTreeInfo(&node->node_group, compare_function);
790 /* ========================================================================= */
791 /* some stuff from "files.c" */
792 /* ========================================================================= */
794 #if defined(PLATFORM_WIN32)
796 #define S_IRGRP S_IRUSR
799 #define S_IROTH S_IRUSR
802 #define S_IWGRP S_IWUSR
805 #define S_IWOTH S_IWUSR
808 #define S_IXGRP S_IXUSR
811 #define S_IXOTH S_IXUSR
814 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
819 #endif /* PLATFORM_WIN32 */
821 /* file permissions for newly written files */
822 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
823 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
824 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
826 #define MODE_W_PRIVATE (S_IWUSR)
827 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
828 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
830 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
831 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
833 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
834 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
836 char *getUserDataDir(void)
838 static char *userdata_dir = NULL;
842 char *home_dir = getHomeDir();
843 char *data_dir = program.userdata_directory;
845 userdata_dir = getPath2(home_dir, data_dir);
853 return getUserDataDir();
856 static mode_t posix_umask(mode_t mask)
858 #if defined(PLATFORM_UNIX)
865 static int posix_mkdir(const char *pathname, mode_t mode)
867 #if defined(PLATFORM_WIN32)
868 return mkdir(pathname);
870 return mkdir(pathname, mode);
874 void createDirectory(char *dir, char *text, int permission_class)
876 /* leave "other" permissions in umask untouched, but ensure group parts
877 of USERDATA_DIR_MODE are not masked */
878 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
879 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
880 mode_t normal_umask = posix_umask(0);
881 mode_t group_umask = ~(dir_mode & S_IRWXG);
882 posix_umask(normal_umask & group_umask);
884 if (access(dir, F_OK) != 0)
885 if (posix_mkdir(dir, dir_mode) != 0)
886 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
888 posix_umask(normal_umask); /* reset normal umask */
891 void InitUserDataDirectory()
893 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
896 void SetFilePermissions(char *filename, int permission_class)
898 chmod(filename, (permission_class == PERMS_PRIVATE ?
899 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
902 char *getCookie(char *file_type)
904 static char cookie[MAX_COOKIE_LEN + 1];
906 if (strlen(program.cookie_prefix) + 1 +
907 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
908 return "[COOKIE ERROR]"; /* should never happen */
910 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
911 program.cookie_prefix, file_type,
912 program.version_major, program.version_minor);
917 int getFileVersionFromCookieString(const char *cookie)
919 const char *ptr_cookie1, *ptr_cookie2;
920 const char *pattern1 = "_FILE_VERSION_";
921 const char *pattern2 = "?.?";
922 const int len_cookie = strlen(cookie);
923 const int len_pattern1 = strlen(pattern1);
924 const int len_pattern2 = strlen(pattern2);
925 const int len_pattern = len_pattern1 + len_pattern2;
926 int version_major, version_minor;
928 if (len_cookie <= len_pattern)
931 ptr_cookie1 = &cookie[len_cookie - len_pattern];
932 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
934 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
937 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
938 ptr_cookie2[1] != '.' ||
939 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
942 version_major = ptr_cookie2[0] - '0';
943 version_minor = ptr_cookie2[2] - '0';
945 return VERSION_IDENT(version_major, version_minor, 0);
948 boolean checkCookieString(const char *cookie, const char *template)
950 const char *pattern = "_FILE_VERSION_?.?";
951 const int len_cookie = strlen(cookie);
952 const int len_template = strlen(template);
953 const int len_pattern = strlen(pattern);
955 if (len_cookie != len_template)
958 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
964 /* ------------------------------------------------------------------------- */
965 /* setup file list handling functions */
966 /* ------------------------------------------------------------------------- */
968 int get_string_integer_value(char *s)
970 static char *number_text[][3] =
972 { "0", "zero", "null", },
973 { "1", "one", "first" },
974 { "2", "two", "second" },
975 { "3", "three", "third" },
976 { "4", "four", "fourth" },
977 { "5", "five", "fifth" },
978 { "6", "six", "sixth" },
979 { "7", "seven", "seventh" },
980 { "8", "eight", "eighth" },
981 { "9", "nine", "ninth" },
982 { "10", "ten", "tenth" },
983 { "11", "eleven", "eleventh" },
984 { "12", "twelve", "twelfth" },
988 char *s_lower = getStringToLower(s);
993 if (strcmp(s_lower, number_text[i][j]) == 0)
1004 boolean get_string_boolean_value(char *s)
1006 char *s_lower = getStringToLower(s);
1007 boolean result = FALSE;
1009 if (strcmp(s_lower, "true") == 0 ||
1010 strcmp(s_lower, "yes") == 0 ||
1011 strcmp(s_lower, "on") == 0 ||
1012 get_string_integer_value(s) == 1)
1020 char *getFormattedSetupEntry(char *token, char *value)
1023 static char entry[MAX_LINE_LEN];
1025 /* start with the token and some spaces to format output line */
1026 sprintf(entry, "%s:", token);
1027 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1030 /* continue with the token's value */
1031 strcat(entry, value);
1036 void freeSetupFileList(struct SetupFileList *setup_file_list)
1038 if (!setup_file_list)
1041 if (setup_file_list->token)
1042 free(setup_file_list->token);
1043 if (setup_file_list->value)
1044 free(setup_file_list->value);
1045 if (setup_file_list->next)
1046 freeSetupFileList(setup_file_list->next);
1047 free(setup_file_list);
1050 static struct SetupFileList *newSetupFileList(char *token, char *value)
1052 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1054 new->token = checked_malloc(strlen(token) + 1);
1055 strcpy(new->token, token);
1057 new->value = checked_malloc(strlen(value) + 1);
1058 strcpy(new->value, value);
1065 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1067 if (!setup_file_list)
1070 if (strcmp(setup_file_list->token, token) == 0)
1071 return setup_file_list->value;
1073 return getTokenValue(setup_file_list->next, token);
1076 static void setTokenValue(struct SetupFileList *setup_file_list,
1077 char *token, char *value)
1079 if (!setup_file_list)
1082 if (strcmp(setup_file_list->token, token) == 0)
1084 free(setup_file_list->value);
1085 setup_file_list->value = checked_malloc(strlen(value) + 1);
1086 strcpy(setup_file_list->value, value);
1088 else if (setup_file_list->next == NULL)
1089 setup_file_list->next = newSetupFileList(token, value);
1091 setTokenValue(setup_file_list->next, token, value);
1095 static void printSetupFileList(struct SetupFileList *setup_file_list)
1097 if (!setup_file_list)
1100 printf("token: '%s'\n", setup_file_list->token);
1101 printf("value: '%s'\n", setup_file_list->value);
1103 printSetupFileList(setup_file_list->next);
1107 struct SetupFileList *loadSetupFileList(char *filename)
1110 char line[MAX_LINE_LEN];
1111 char *token, *value, *line_ptr;
1112 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1113 struct SetupFileList *first_valid_list_entry;
1117 if (!(file = fopen(filename, MODE_READ)))
1119 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1125 /* read next line of input file */
1126 if (!fgets(line, MAX_LINE_LEN, file))
1129 /* cut trailing comment or whitespace from input line */
1130 for (line_ptr = line; *line_ptr; line_ptr++)
1132 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1139 /* cut trailing whitespaces from input line */
1140 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1141 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1144 /* ignore empty lines */
1148 line_len = strlen(line);
1150 /* cut leading whitespaces from token */
1151 for (token = line; *token; token++)
1152 if (*token != ' ' && *token != '\t')
1155 /* find end of token */
1156 for (line_ptr = token; *line_ptr; line_ptr++)
1158 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1165 if (line_ptr < line + line_len)
1166 value = line_ptr + 1;
1170 /* cut leading whitespaces from value */
1171 for (; *value; value++)
1172 if (*value != ' ' && *value != '\t')
1175 if (*token && *value)
1176 setTokenValue(setup_file_list, token, value);
1181 first_valid_list_entry = setup_file_list->next;
1183 /* free empty list header */
1184 setup_file_list->next = NULL;
1185 freeSetupFileList(setup_file_list);
1187 if (first_valid_list_entry == NULL)
1188 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1190 return first_valid_list_entry;
1193 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1196 if (!setup_file_list)
1199 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1201 if (!checkCookieString(setup_file_list->value, identifier))
1203 Error(ERR_WARN, "configuration file has wrong file identifier");
1210 if (setup_file_list->next)
1211 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1214 Error(ERR_WARN, "configuration file has no file identifier");
1220 /* ========================================================================= */
1221 /* setup file stuff */
1222 /* ========================================================================= */
1224 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1225 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1226 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1228 /* level directory info */
1229 #define LEVELINFO_TOKEN_IDENTIFIER 0
1230 #define LEVELINFO_TOKEN_NAME 1
1231 #define LEVELINFO_TOKEN_NAME_SORTING 2
1232 #define LEVELINFO_TOKEN_AUTHOR 3
1233 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1234 #define LEVELINFO_TOKEN_LEVELS 5
1235 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1236 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1237 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1238 #define LEVELINFO_TOKEN_READONLY 9
1239 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1240 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1241 #define LEVELINFO_TOKEN_MUSIC_SET 12
1243 #define NUM_LEVELINFO_TOKENS 13
1245 static LevelDirTree ldi;
1247 static struct TokenInfo levelinfo_tokens[] =
1249 /* level directory info */
1250 { TYPE_STRING, &ldi.identifier, "identifier" },
1251 { TYPE_STRING, &ldi.name, "name" },
1252 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1253 { TYPE_STRING, &ldi.author, "author" },
1254 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1255 { TYPE_INTEGER, &ldi.levels, "levels" },
1256 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1257 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1258 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1259 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1260 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1261 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1262 { TYPE_STRING, &ldi.music_set, "music_set" }
1265 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1269 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1270 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1271 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1272 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1275 ldi->node_parent = NULL;
1276 ldi->node_group = NULL;
1280 ldi->cl_cursor = -1;
1282 ldi->filename = NULL;
1283 ldi->fullpath = NULL;
1284 ldi->basepath = NULL;
1285 ldi->identifier = NULL;
1286 ldi->name = getStringCopy(ANONYMOUS_NAME);
1287 ldi->name_sorting = NULL;
1288 ldi->author = getStringCopy(ANONYMOUS_NAME);
1290 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1291 ldi->parent_link = FALSE;
1292 ldi->user_defined = FALSE;
1294 ldi->class_desc = NULL;
1296 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1298 ldi->imported_from = NULL;
1299 ldi->graphics_set = NULL;
1300 ldi->sounds_set = NULL;
1301 ldi->music_set = NULL;
1302 ldi->graphics_path = getStringCopy(NOT_AVAILABLE);
1303 ldi->sounds_path = getStringCopy(NOT_AVAILABLE);
1304 ldi->music_path = getStringCopy(NOT_AVAILABLE);
1306 ldi->first_level = 0;
1307 ldi->last_level = 0;
1308 ldi->level_group = FALSE;
1309 ldi->handicap_level = 0;
1310 ldi->readonly = TRUE;
1314 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1318 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1320 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1324 /* first copy all values from the parent structure ... */
1327 /* ... then set all fields to default that cannot be inherited from parent.
1328 This is especially important for all those fields that can be set from
1329 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1330 calls 'free()' for all already set token values which requires that no
1331 other structure's pointer may point to them!
1334 ldi->filename = NULL;
1335 ldi->fullpath = NULL;
1336 ldi->basepath = NULL;
1337 ldi->identifier = NULL;
1338 ldi->name = getStringCopy(ANONYMOUS_NAME);
1339 ldi->name_sorting = NULL;
1340 ldi->author = getStringCopy(parent->author);
1341 ldi->imported_from = getStringCopy(parent->imported_from);
1343 ldi->level_group = FALSE;
1344 ldi->parent_link = FALSE;
1346 ldi->node_top = parent->node_top;
1347 ldi->node_parent = parent;
1348 ldi->node_group = NULL;
1352 void setSetupInfo(struct TokenInfo *token_info,
1353 int token_nr, char *token_value)
1355 int token_type = token_info[token_nr].type;
1356 void *setup_value = token_info[token_nr].value;
1358 if (token_value == NULL)
1361 /* set setup field to corresponding token value */
1366 *(boolean *)setup_value = get_string_boolean_value(token_value);
1370 *(Key *)setup_value = getKeyFromKeyName(token_value);
1374 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1378 *(int *)setup_value = get_string_integer_value(token_value);
1382 if (*(char **)setup_value != NULL)
1383 free(*(char **)setup_value);
1384 *(char **)setup_value = getStringCopy(token_value);
1392 static int compareTreeInfoEntries(const void *object1, const void *object2)
1394 const TreeInfo *entry1 = *((TreeInfo **)object1);
1395 const TreeInfo *entry2 = *((TreeInfo **)object2);
1396 int class_sorting1, class_sorting2;
1399 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1401 class_sorting1 = LEVELSORTING(entry1);
1402 class_sorting2 = LEVELSORTING(entry2);
1406 class_sorting1 = ARTWORKSORTING(entry1);
1407 class_sorting2 = ARTWORKSORTING(entry2);
1410 if (entry1->parent_link || entry2->parent_link)
1411 compare_result = (entry1->parent_link ? -1 : +1);
1412 else if (entry1->sort_priority == entry2->sort_priority)
1414 char *name1 = getStringToLower(entry1->name_sorting);
1415 char *name2 = getStringToLower(entry2->name_sorting);
1417 compare_result = strcmp(name1, name2);
1422 else if (class_sorting1 == class_sorting2)
1423 compare_result = entry1->sort_priority - entry2->sort_priority;
1425 compare_result = class_sorting1 - class_sorting2;
1427 return compare_result;
1430 static void createParentTreeInfoNode(TreeInfo *node_parent)
1434 if (node_parent == NULL)
1437 ti_new = newTreeInfo();
1438 setTreeInfoToDefaults(ti_new, node_parent->type);
1440 ti_new->node_parent = node_parent;
1441 ti_new->parent_link = TRUE;
1443 ti_new->identifier = getStringCopy(node_parent->identifier);
1444 ti_new->name = ".. (parent directory)";
1445 ti_new->name_sorting = getStringCopy(ti_new->name);
1447 ti_new->filename = "..";
1448 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1450 ti_new->sort_priority = node_parent->sort_priority;
1451 ti_new->class_desc = getLevelClassDescription(ti_new);
1453 pushTreeInfo(&node_parent->node_group, ti_new);
1456 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1457 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1459 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1460 TreeInfo *node_parent,
1461 char *level_directory,
1462 char *directory_name)
1464 char *directory_path = getPath2(level_directory, directory_name);
1465 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1466 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1467 LevelDirTree *leveldir_new = NULL;
1470 if (setup_file_list == NULL)
1472 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1474 free(directory_path);
1480 leveldir_new = newTreeInfo();
1483 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1485 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1487 leveldir_new->filename = getStringCopy(directory_name);
1489 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1491 /* set all structure fields according to the token/value pairs */
1492 ldi = *leveldir_new;
1493 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1494 setSetupInfo(levelinfo_tokens, i,
1495 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1496 *leveldir_new = ldi;
1498 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1500 free(leveldir_new->name);
1501 leveldir_new->name = getStringCopy(leveldir_new->filename);
1504 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1506 if (leveldir_new->identifier == NULL)
1507 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1509 if (leveldir_new->name_sorting == NULL)
1510 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1512 if (node_parent == NULL) /* top level group */
1514 leveldir_new->basepath = level_directory;
1515 leveldir_new->fullpath = leveldir_new->filename;
1517 else /* sub level group */
1519 leveldir_new->basepath = node_parent->basepath;
1520 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1523 if (leveldir_new->levels < 1)
1524 leveldir_new->levels = 1;
1526 leveldir_new->last_level =
1527 leveldir_new->first_level + leveldir_new->levels - 1;
1529 leveldir_new->user_defined =
1530 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1532 leveldir_new->color = LEVELCOLOR(leveldir_new);
1533 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1535 leveldir_new->handicap_level = /* set handicap to default value */
1536 (leveldir_new->user_defined ?
1537 leveldir_new->last_level :
1538 leveldir_new->first_level);
1540 pushTreeInfo(node_first, leveldir_new);
1542 freeSetupFileList(setup_file_list);
1544 if (leveldir_new->level_group)
1546 /* create node to link back to current level directory */
1547 createParentTreeInfoNode(leveldir_new);
1549 /* step into sub-directory and look for more level series */
1550 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1551 leveldir_new, directory_path);
1554 free(directory_path);
1560 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1561 TreeInfo *node_parent,
1562 char *level_directory)
1565 struct dirent *dir_entry;
1566 boolean valid_entry_found = FALSE;
1568 if ((dir = opendir(level_directory)) == NULL)
1570 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1574 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1576 struct stat file_status;
1577 char *directory_name = dir_entry->d_name;
1578 char *directory_path = getPath2(level_directory, directory_name);
1580 /* skip entries for current and parent directory */
1581 if (strcmp(directory_name, ".") == 0 ||
1582 strcmp(directory_name, "..") == 0)
1584 free(directory_path);
1588 /* find out if directory entry is itself a directory */
1589 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1590 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1592 free(directory_path);
1596 free(directory_path);
1598 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1599 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1600 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1603 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1610 if (!valid_entry_found)
1612 /* check if this directory directly contains a file "levelinfo.conf" */
1613 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1614 level_directory, ".");
1617 if (!valid_entry_found)
1618 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1622 void LoadLevelInfo()
1624 InitUserLevelDirectory(getLoginName());
1626 DrawInitText("Loading level series:", 120, FC_GREEN);
1628 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1629 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1631 /* before sorting, the first entries will be from the user directory */
1632 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1634 if (leveldir_first == NULL)
1635 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1637 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1640 dumpTreeInfo(leveldir_first, 0);
1644 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1645 TreeInfo *node_parent,
1646 char *base_directory,
1647 char *directory_name, int type)
1649 char *directory_path = getPath2(base_directory, directory_name);
1650 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1651 struct SetupFileList *setup_file_list = NULL;
1652 TreeInfo *artwork_new = NULL;
1655 if (access(filename, F_OK) == 0) /* file exists */
1656 setup_file_list = loadSetupFileList(filename);
1658 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1661 struct dirent *dir_entry;
1662 boolean valid_file_found = FALSE;
1664 if ((dir = opendir(directory_path)) != NULL)
1666 while ((dir_entry = readdir(dir)) != NULL)
1668 char *entry_name = dir_entry->d_name;
1670 if (FileIsArtworkType(entry_name, type))
1672 valid_file_found = TRUE;
1680 if (!valid_file_found)
1682 if (strcmp(directory_name, ".") != 0)
1683 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1685 free(directory_path);
1692 artwork_new = newTreeInfo();
1695 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1697 setTreeInfoToDefaults(artwork_new, type);
1699 artwork_new->filename = getStringCopy(directory_name);
1701 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1704 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1707 /* set all structure fields according to the token/value pairs */
1709 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1710 setSetupInfo(levelinfo_tokens, i,
1711 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1714 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1716 free(artwork_new->name);
1717 artwork_new->name = getStringCopy(artwork_new->filename);
1721 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1724 if (artwork_new->identifier == NULL)
1725 artwork_new->identifier = getStringCopy(artwork_new->filename);
1727 if (artwork_new->name_sorting == NULL)
1728 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1731 if (node_parent == NULL) /* top level group */
1733 artwork_new->basepath = getStringCopy(base_directory);
1734 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1736 else /* sub level group */
1738 artwork_new->basepath = getStringCopy(node_parent->basepath);
1739 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1742 artwork_new->user_defined =
1743 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1745 /* (may use ".sort_priority" from "setup_file_list" above) */
1746 artwork_new->color = ARTWORKCOLOR(artwork_new);
1747 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1749 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1751 if (artwork_new->name != NULL)
1752 free(artwork_new->name);
1754 if (strcmp(artwork_new->filename, ".") == 0)
1756 if (artwork_new->user_defined)
1758 artwork_new->identifier = getStringCopy("private");
1759 artwork_new->sort_priority = ARTWORKCLASS_USER;
1763 artwork_new->identifier = getStringCopy("classic");
1764 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1767 /* set to new values after changing ".sort_priority" */
1768 artwork_new->color = ARTWORKCOLOR(artwork_new);
1769 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1773 artwork_new->identifier = getStringCopy(artwork_new->filename);
1776 artwork_new->name = getStringCopy(artwork_new->identifier);
1777 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1780 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1782 pushTreeInfo(node_first, artwork_new);
1784 freeSetupFileList(setup_file_list);
1786 free(directory_path);
1792 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1793 TreeInfo *node_parent,
1794 char *base_directory, int type)
1797 struct dirent *dir_entry;
1798 boolean valid_entry_found = FALSE;
1800 if ((dir = opendir(base_directory)) == NULL)
1802 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1803 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1807 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1809 struct stat file_status;
1810 char *directory_name = dir_entry->d_name;
1811 char *directory_path = getPath2(base_directory, directory_name);
1813 /* skip entries for current and parent directory */
1814 if (strcmp(directory_name, ".") == 0 ||
1815 strcmp(directory_name, "..") == 0)
1817 free(directory_path);
1821 /* find out if directory entry is itself a directory */
1822 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1823 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1825 free(directory_path);
1829 free(directory_path);
1831 /* check if this directory contains artwork with or without config file */
1832 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1834 directory_name, type);
1839 /* check if this directory directly contains artwork itself */
1840 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1841 base_directory, ".",
1843 if (!valid_entry_found)
1844 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1848 static TreeInfo *getDummyArtworkInfo(int type)
1850 /* this is only needed when there is completely no artwork available */
1851 TreeInfo *artwork_new = newTreeInfo();
1853 setTreeInfoToDefaults(artwork_new, type);
1855 artwork_new->filename = getStringCopy(NOT_AVAILABLE);
1856 artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
1857 artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
1859 if (artwork_new->name != NULL)
1860 free(artwork_new->name);
1862 artwork_new->identifier = getStringCopy(NOT_AVAILABLE);
1863 artwork_new->name = getStringCopy(NOT_AVAILABLE);
1864 artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
1869 void LoadArtworkInfo()
1871 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1873 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1874 options.graphics_directory,
1875 TREE_TYPE_GRAPHICS_DIR);
1876 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1877 getUserGraphicsDir(),
1878 TREE_TYPE_GRAPHICS_DIR);
1880 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1881 options.sounds_directory,
1882 TREE_TYPE_SOUNDS_DIR);
1883 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1885 TREE_TYPE_SOUNDS_DIR);
1887 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1888 options.music_directory,
1889 TREE_TYPE_MUSIC_DIR);
1890 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1892 TREE_TYPE_MUSIC_DIR);
1894 if (artwork.gfx_first == NULL)
1895 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1896 if (artwork.snd_first == NULL)
1897 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1898 if (artwork.mus_first == NULL)
1899 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1901 /* before sorting, the first entries will be from the user directory */
1902 artwork.gfx_current =
1903 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1904 if (artwork.gfx_current == NULL)
1905 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1907 artwork.snd_current =
1908 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1909 if (artwork.snd_current == NULL)
1910 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1912 artwork.mus_current =
1913 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1914 if (artwork.mus_current == NULL)
1915 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1917 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1918 artwork.snd_current_identifier = artwork.snd_current->identifier;
1919 artwork.mus_current_identifier = artwork.mus_current->identifier;
1922 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1923 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1924 printf("music set == %s\n\n", artwork.mus_current_identifier);
1927 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1928 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1929 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1932 dumpTreeInfo(artwork.gfx_first, 0);
1933 dumpTreeInfo(artwork.snd_first, 0);
1934 dumpTreeInfo(artwork.mus_first, 0);
1938 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1939 LevelDirTree *level_node)
1941 /* recursively check all level directories for artwork sub-directories */
1945 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1946 ARTWORK_DIRECTORY((*artwork_node)->type));
1949 if (!level_node->parent_link)
1950 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1951 level_node->filename, level_node->name);
1954 if (!level_node->parent_link)
1956 TreeInfo *topnode_last = *artwork_node;
1958 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1959 (*artwork_node)->type);
1961 if (topnode_last != *artwork_node)
1963 free((*artwork_node)->identifier);
1964 free((*artwork_node)->name);
1965 free((*artwork_node)->name_sorting);
1967 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1968 (*artwork_node)->name = getStringCopy(level_node->name);
1969 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1971 (*artwork_node)->sort_priority = level_node->sort_priority;
1972 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1978 if (level_node->node_group != NULL)
1979 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1981 level_node = level_node->next;
1985 void LoadLevelArtworkInfo()
1987 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1989 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1990 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1991 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1993 /* needed for reloading level artwork not known at ealier stage */
1994 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
1996 artwork.gfx_current =
1997 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1998 if (artwork.gfx_current == NULL)
1999 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2002 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2004 artwork.snd_current =
2005 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2006 if (artwork.snd_current == NULL)
2007 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2010 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2012 artwork.mus_current =
2013 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2014 if (artwork.mus_current == NULL)
2015 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2018 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2019 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2020 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2023 dumpTreeInfo(artwork.gfx_first, 0);
2024 dumpTreeInfo(artwork.snd_first, 0);
2025 dumpTreeInfo(artwork.mus_first, 0);
2029 static void SaveUserLevelInfo()
2035 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2037 if (!(file = fopen(filename, MODE_WRITE)))
2039 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2044 /* always start with reliable default values */
2045 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2047 ldi.name = getStringCopy(getLoginName());
2048 ldi.author = getStringCopy(getRealName());
2050 ldi.first_level = 1;
2051 ldi.sort_priority = LEVELCLASS_USER_START;
2052 ldi.readonly = FALSE;
2054 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2055 getCookie("LEVELINFO")));
2057 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2058 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2059 i != LEVELINFO_TOKEN_NAME_SORTING &&
2060 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2061 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2066 SetFilePermissions(filename, PERMS_PRIVATE);
2069 char *getSetupValue(int type, void *value)
2071 static char value_string[MAX_LINE_LEN];
2079 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2083 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2087 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2091 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2095 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2099 sprintf(value_string, "%d", *(int *)value);
2103 strcpy(value_string, *(char **)value);
2107 value_string[0] = '\0';
2111 return value_string;
2114 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2118 static char token_string[MAX_LINE_LEN];
2119 int token_type = token_info[token_nr].type;
2120 void *setup_value = token_info[token_nr].value;
2121 char *token_text = token_info[token_nr].text;
2122 char *value_string = getSetupValue(token_type, setup_value);
2124 /* build complete token string */
2125 sprintf(token_string, "%s%s", prefix, token_text);
2127 /* build setup entry line */
2128 line = getFormattedSetupEntry(token_string, value_string);
2130 if (token_type == TYPE_KEY_X11)
2132 Key key = *(Key *)setup_value;
2133 char *keyname = getKeyNameFromKey(key);
2135 /* add comment, if useful */
2136 if (strcmp(keyname, "(undefined)") != 0 &&
2137 strcmp(keyname, "(unknown)") != 0)
2139 /* add at least one whitespace */
2141 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2145 strcat(line, keyname);
2152 void LoadLevelSetup_LastSeries()
2155 struct SetupFileList *level_setup_list = NULL;
2157 /* always start with reliable default values */
2158 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2160 /* ----------------------------------------------------------------------- */
2161 /* ~/.<program>/levelsetup.conf */
2162 /* ----------------------------------------------------------------------- */
2164 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2166 if ((level_setup_list = loadSetupFileList(filename)))
2168 char *last_level_series =
2169 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2171 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2173 if (leveldir_current == NULL)
2174 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2176 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2178 freeSetupFileList(level_setup_list);
2181 Error(ERR_WARN, "using default setup values");
2186 void SaveLevelSetup_LastSeries()
2189 char *level_subdir = leveldir_current->filename;
2192 /* ----------------------------------------------------------------------- */
2193 /* ~/.<program>/levelsetup.conf */
2194 /* ----------------------------------------------------------------------- */
2196 InitUserDataDirectory();
2198 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2200 if (!(file = fopen(filename, MODE_WRITE)))
2202 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2207 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2208 getCookie("LEVELSETUP")));
2209 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2215 SetFilePermissions(filename, PERMS_PRIVATE);
2218 static void checkSeriesInfo()
2220 static char *level_directory = NULL;
2222 struct dirent *dir_entry;
2224 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2226 level_directory = getPath2((leveldir_current->user_defined ?
2227 getUserLevelDir(NULL) :
2228 options.level_directory),
2229 leveldir_current->fullpath);
2231 if ((dir = opendir(level_directory)) == NULL)
2233 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2237 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2239 if (strlen(dir_entry->d_name) > 4 &&
2240 dir_entry->d_name[3] == '.' &&
2241 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2243 char levelnum_str[4];
2246 strncpy(levelnum_str, dir_entry->d_name, 3);
2247 levelnum_str[3] = '\0';
2249 levelnum_value = atoi(levelnum_str);
2251 if (levelnum_value < leveldir_current->first_level)
2253 Error(ERR_WARN, "additional level %d found", levelnum_value);
2254 leveldir_current->first_level = levelnum_value;
2256 else if (levelnum_value > leveldir_current->last_level)
2258 Error(ERR_WARN, "additional level %d found", levelnum_value);
2259 leveldir_current->last_level = levelnum_value;
2267 void LoadLevelSetup_SeriesInfo()
2270 struct SetupFileList *level_setup_list = NULL;
2271 char *level_subdir = leveldir_current->filename;
2273 /* always start with reliable default values */
2274 level_nr = leveldir_current->first_level;
2276 checkSeriesInfo(leveldir_current);
2278 /* ----------------------------------------------------------------------- */
2279 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2280 /* ----------------------------------------------------------------------- */
2282 level_subdir = leveldir_current->filename;
2284 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2286 if ((level_setup_list = loadSetupFileList(filename)))
2290 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2294 level_nr = atoi(token_value);
2296 if (level_nr < leveldir_current->first_level)
2297 level_nr = leveldir_current->first_level;
2298 if (level_nr > leveldir_current->last_level)
2299 level_nr = leveldir_current->last_level;
2302 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2306 int level_nr = atoi(token_value);
2308 if (level_nr < leveldir_current->first_level)
2309 level_nr = leveldir_current->first_level;
2310 if (level_nr > leveldir_current->last_level + 1)
2311 level_nr = leveldir_current->last_level;
2313 if (leveldir_current->user_defined)
2314 level_nr = leveldir_current->last_level;
2316 leveldir_current->handicap_level = level_nr;
2319 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2321 freeSetupFileList(level_setup_list);
2324 Error(ERR_WARN, "using default setup values");
2329 void SaveLevelSetup_SeriesInfo()
2332 char *level_subdir = leveldir_current->filename;
2333 char *level_nr_str = int2str(level_nr, 0);
2334 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2337 /* ----------------------------------------------------------------------- */
2338 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2339 /* ----------------------------------------------------------------------- */
2341 InitLevelSetupDirectory(level_subdir);
2343 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2345 if (!(file = fopen(filename, MODE_WRITE)))
2347 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2352 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2353 getCookie("LEVELSETUP")));
2354 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2356 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2357 handicap_level_str));
2362 SetFilePermissions(filename, PERMS_PRIVATE);