1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
25 /* file names and filename extensions */
26 #if !defined(PLATFORM_MSDOS)
27 #define LEVELSETUP_DIRECTORY "levelsetup"
28 #define SETUP_FILENAME "setup.conf"
29 #define LEVELSETUP_FILENAME "levelsetup.conf"
30 #define LEVELINFO_FILENAME "levelinfo.conf"
31 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
32 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
33 #define MUSICINFO_FILENAME "musicinfo.conf"
34 #define LEVELFILE_EXTENSION "level"
35 #define TAPEFILE_EXTENSION "tape"
36 #define SCOREFILE_EXTENSION "score"
38 #define LEVELSETUP_DIRECTORY "lvlsetup"
39 #define SETUP_FILENAME "setup.cnf"
40 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
41 #define LEVELINFO_FILENAME "lvlinfo.cnf"
42 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
43 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
44 #define MUSICINFO_FILENAME "musinfo.cnf"
45 #define LEVELFILE_EXTENSION "lvl"
46 #define TAPEFILE_EXTENSION "tap"
47 #define SCOREFILE_EXTENSION "sco"
50 #define NUM_LEVELCLASS_DESC 8
51 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
63 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
64 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
65 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
66 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
67 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
68 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
69 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
70 IS_LEVELCLASS_USER(n) ? FC_RED : \
73 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
74 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
75 IS_LEVELCLASS_BD(n) ? 2 : \
76 IS_LEVELCLASS_EM(n) ? 3 : \
77 IS_LEVELCLASS_SP(n) ? 4 : \
78 IS_LEVELCLASS_DX(n) ? 5 : \
79 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
80 IS_LEVELCLASS_USER(n) ? 7 : \
83 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
84 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
85 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
86 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
89 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
90 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
91 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
92 IS_ARTWORKCLASS_USER(n) ? 3 : \
95 #define TOKEN_VALUE_POSITION 40
96 #define TOKEN_COMMENT_POSITION 60
98 #define MAX_COOKIE_LEN 256
100 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
101 GRAPHICSINFO_FILENAME : \
102 (type) == TREE_TYPE_SOUNDS_DIR ? \
103 SOUNDSINFO_FILENAME : \
104 (type) == TREE_TYPE_MUSIC_DIR ? \
105 MUSICINFO_FILENAME : "")
107 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
108 GRAPHICS_DIRECTORY : \
109 (type) == TREE_TYPE_SOUNDS_DIR ? \
111 (type) == TREE_TYPE_MUSIC_DIR ? \
112 MUSIC_DIRECTORY : "")
114 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
115 options.graphics_directory : \
116 (type) == TREE_TYPE_SOUNDS_DIR ? \
117 options.sounds_directory : \
118 (type) == TREE_TYPE_MUSIC_DIR ? \
119 options.music_directory : "")
122 /* ------------------------------------------------------------------------- */
124 /* ------------------------------------------------------------------------- */
126 static char *getLevelClassDescription(TreeInfo *ldi)
128 int position = ldi->sort_priority / 100;
130 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
131 return levelclass_desc[position];
133 return "Unknown Level Class";
136 static char *getUserLevelDir(char *level_subdir)
138 static char *userlevel_dir = NULL;
139 char *data_dir = getUserDataDir();
140 char *userlevel_subdir = LEVELS_DIRECTORY;
145 if (level_subdir != NULL)
146 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
148 userlevel_dir = getPath2(data_dir, userlevel_subdir);
150 return userlevel_dir;
153 static char *getTapeDir(char *level_subdir)
155 static char *tape_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *tape_subdir = TAPES_DIRECTORY;
162 if (level_subdir != NULL)
163 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
165 tape_dir = getPath2(data_dir, tape_subdir);
170 static char *getScoreDir(char *level_subdir)
172 static char *score_dir = NULL;
173 char *data_dir = options.rw_base_directory;
174 char *score_subdir = SCORES_DIRECTORY;
179 if (level_subdir != NULL)
180 score_dir = getPath3(data_dir, score_subdir, level_subdir);
182 score_dir = getPath2(data_dir, score_subdir);
187 static char *getLevelSetupDir(char *level_subdir)
189 static char *levelsetup_dir = NULL;
190 char *data_dir = getUserDataDir();
191 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
194 free(levelsetup_dir);
196 if (level_subdir != NULL)
197 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
199 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
201 return levelsetup_dir;
204 static char *getLevelDirFromTreeInfo(TreeInfo *node)
206 static char *level_dir = NULL;
209 return options.level_directory;
214 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
215 options.level_directory), node->fullpath);
220 static char *getCurrentLevelDir()
222 return getLevelDirFromTreeInfo(leveldir_current);
225 static char *getDefaultGraphicsDir(char *graphics_subdir)
227 static char *graphics_dir = NULL;
229 if (graphics_subdir == NULL)
230 return options.graphics_directory;
235 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
240 static char *getDefaultSoundsDir(char *sounds_subdir)
242 static char *sounds_dir = NULL;
244 if (sounds_subdir == NULL)
245 return options.sounds_directory;
250 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
255 static char *getDefaultMusicDir(char *music_subdir)
257 static char *music_dir = NULL;
259 if (music_subdir == NULL)
260 return options.music_directory;
265 music_dir = getPath2(options.music_directory, music_subdir);
270 static char *getUserGraphicsDir()
272 static char *usergraphics_dir = NULL;
274 if (usergraphics_dir == NULL)
275 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
277 return usergraphics_dir;
280 static char *getUserSoundsDir()
282 static char *usersounds_dir = NULL;
284 if (usersounds_dir == NULL)
285 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
287 return usersounds_dir;
290 static char *getUserMusicDir()
292 static char *usermusic_dir = NULL;
294 if (usermusic_dir == NULL)
295 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
297 return usermusic_dir;
300 static char *getSetupArtworkDir(TreeInfo *ti)
302 static char *artwork_dir = NULL;
304 if (artwork_dir != NULL)
307 artwork_dir = getPath2(ti->basepath, ti->fullpath);
312 void setLevelArtworkDir(TreeInfo *ti)
314 char **artwork_path_ptr, *artwork_set;
315 TreeInfo *level_artwork;
317 if (ti == NULL || leveldir_current == NULL)
321 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
322 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
323 &leveldir_current->music_path);
326 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
327 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
328 leveldir_current->music_set);
330 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)) == NULL)
333 if (*artwork_path_ptr != NULL)
334 free(*artwork_path_ptr);
336 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
339 static char *getLevelArtworkDir(int type)
343 if (leveldir_current == NULL)
344 return UNDEFINED_FILENAME;
347 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
348 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
349 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
355 char *getLevelFilename(int nr)
357 static char *filename = NULL;
358 char basename[MAX_FILENAME_LEN];
360 if (filename != NULL)
363 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
364 filename = getPath2(getCurrentLevelDir(), basename);
369 char *getTapeFilename(int nr)
371 static char *filename = NULL;
372 char basename[MAX_FILENAME_LEN];
374 if (filename != NULL)
377 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
378 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
383 char *getScoreFilename(int nr)
385 static char *filename = NULL;
386 char basename[MAX_FILENAME_LEN];
388 if (filename != NULL)
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 if (filename != NULL)
404 filename = getPath2(getSetupDir(), SETUP_FILENAME);
409 static char *getCorrectedImageBasename(char *basename)
411 char *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 *getCustomArtworkFilename(char *basename, int type)
515 if (type == ARTWORK_TYPE_GRAPHICS)
516 return getCustomImageFilename(basename);
517 else if (type == ARTWORK_TYPE_SOUNDS)
518 return getCustomSoundFilename(basename);
520 return UNDEFINED_FILENAME;
523 char *getCustomArtworkConfigFilename(int type)
525 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
528 char *getCustomMusicDirectory(void)
530 static char *directory = NULL;
532 if (directory != NULL)
535 if (!setup.override_level_music)
537 /* 1st try: look for special artwork configured in level series config */
538 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
539 if (fileExists(directory))
542 /* 2nd try: look for special artwork in current level series directory */
543 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
544 if (fileExists(directory))
548 /* 3rd try: look for special artwork in configured artwork directory */
549 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
550 if (fileExists(directory))
553 /* 4th try: look for default artwork in new default artwork directory */
554 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
555 if (fileExists(directory))
558 /* 5th try: look for default artwork in old default artwork directory */
559 directory = getStringCopy(options.music_directory);
560 if (fileExists(directory))
563 return NULL; /* cannot find specified artwork file anywhere */
566 void InitTapeDirectory(char *level_subdir)
568 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
569 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
570 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
573 void InitScoreDirectory(char *level_subdir)
575 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
576 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
579 static void SaveUserLevelInfo();
581 void InitUserLevelDirectory(char *level_subdir)
583 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
585 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
586 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
587 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
593 void InitLevelSetupDirectory(char *level_subdir)
595 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
596 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
597 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
601 /* ------------------------------------------------------------------------- */
602 /* some functions to handle lists of level directories */
603 /* ------------------------------------------------------------------------- */
605 TreeInfo *newTreeInfo()
607 return checked_calloc(sizeof(TreeInfo));
610 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
612 node_new->next = *node_first;
613 *node_first = node_new;
616 int numTreeInfo(TreeInfo *node)
629 boolean validLevelSeries(TreeInfo *node)
631 return (node != NULL && !node->node_group && !node->parent_link);
634 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
639 if (node->node_group) /* enter level group (step down into tree) */
640 return getFirstValidTreeInfoEntry(node->node_group);
641 else if (node->parent_link) /* skip start entry of level group */
643 if (node->next) /* get first real level series entry */
644 return getFirstValidTreeInfoEntry(node->next);
645 else /* leave empty level group and go on */
646 return getFirstValidTreeInfoEntry(node->node_parent->next);
648 else /* this seems to be a regular level series */
652 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
657 if (node->node_parent == NULL) /* top level group */
658 return *node->node_top;
659 else /* sub level group */
660 return node->node_parent->node_group;
663 int numTreeInfoInGroup(TreeInfo *node)
665 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
668 int posTreeInfo(TreeInfo *node)
670 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
675 if (node_cmp == node)
679 node_cmp = node_cmp->next;
685 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
687 TreeInfo *node_default = node;
702 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
704 if (identifier == NULL)
709 if (node->node_group)
711 TreeInfo *node_group;
713 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
718 else if (!node->parent_link)
720 if (strcmp(identifier, node->identifier) == 0)
730 void dumpTreeInfo(TreeInfo *node, int depth)
734 printf("Dumping TreeInfo:\n");
738 for (i=0; i<(depth + 1) * 3; i++)
741 printf("filename == '%s' (%s) [%s] (%d)\n",
742 node->filename, node->name, node->identifier, node->sort_priority);
744 if (node->node_group != NULL)
745 dumpTreeInfo(node->node_group, depth + 1);
751 void sortTreeInfo(TreeInfo **node_first,
752 int (*compare_function)(const void *, const void *))
754 int num_nodes = numTreeInfo(*node_first);
755 TreeInfo **sort_array;
756 TreeInfo *node = *node_first;
762 /* allocate array for sorting structure pointers */
763 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
765 /* writing structure pointers to sorting array */
766 while (i < num_nodes && node) /* double boundary check... */
768 sort_array[i] = node;
774 /* sorting the structure pointers in the sorting array */
775 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
778 /* update the linkage of list elements with the sorted node array */
779 for (i=0; i<num_nodes - 1; i++)
780 sort_array[i]->next = sort_array[i + 1];
781 sort_array[num_nodes - 1]->next = NULL;
783 /* update the linkage of the main list anchor pointer */
784 *node_first = sort_array[0];
788 /* now recursively sort the level group structures */
792 if (node->node_group != NULL)
793 sortTreeInfo(&node->node_group, compare_function);
800 /* ========================================================================= */
801 /* some stuff from "files.c" */
802 /* ========================================================================= */
804 #if defined(PLATFORM_WIN32)
806 #define S_IRGRP S_IRUSR
809 #define S_IROTH S_IRUSR
812 #define S_IWGRP S_IWUSR
815 #define S_IWOTH S_IWUSR
818 #define S_IXGRP S_IXUSR
821 #define S_IXOTH S_IXUSR
824 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
829 #endif /* PLATFORM_WIN32 */
831 /* file permissions for newly written files */
832 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
833 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
834 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
836 #define MODE_W_PRIVATE (S_IWUSR)
837 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
838 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
840 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
841 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
843 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
844 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
846 char *getUserDataDir(void)
848 static char *userdata_dir = NULL;
852 char *home_dir = getHomeDir();
853 char *data_dir = program.userdata_directory;
855 userdata_dir = getPath2(home_dir, data_dir);
863 return getUserDataDir();
866 static mode_t posix_umask(mode_t mask)
868 #if defined(PLATFORM_UNIX)
875 static int posix_mkdir(const char *pathname, mode_t mode)
877 #if defined(PLATFORM_WIN32)
878 return mkdir(pathname);
880 return mkdir(pathname, mode);
884 void createDirectory(char *dir, char *text, int permission_class)
886 /* leave "other" permissions in umask untouched, but ensure group parts
887 of USERDATA_DIR_MODE are not masked */
888 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
889 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
890 mode_t normal_umask = posix_umask(0);
891 mode_t group_umask = ~(dir_mode & S_IRWXG);
892 posix_umask(normal_umask & group_umask);
894 if (access(dir, F_OK) != 0)
895 if (posix_mkdir(dir, dir_mode) != 0)
896 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
898 posix_umask(normal_umask); /* reset normal umask */
901 void InitUserDataDirectory()
903 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
906 void SetFilePermissions(char *filename, int permission_class)
908 chmod(filename, (permission_class == PERMS_PRIVATE ?
909 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
912 char *getCookie(char *file_type)
914 static char cookie[MAX_COOKIE_LEN + 1];
916 if (strlen(program.cookie_prefix) + 1 +
917 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
918 return "[COOKIE ERROR]"; /* should never happen */
920 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
921 program.cookie_prefix, file_type,
922 program.version_major, program.version_minor);
927 int getFileVersionFromCookieString(const char *cookie)
929 const char *ptr_cookie1, *ptr_cookie2;
930 const char *pattern1 = "_FILE_VERSION_";
931 const char *pattern2 = "?.?";
932 const int len_cookie = strlen(cookie);
933 const int len_pattern1 = strlen(pattern1);
934 const int len_pattern2 = strlen(pattern2);
935 const int len_pattern = len_pattern1 + len_pattern2;
936 int version_major, version_minor;
938 if (len_cookie <= len_pattern)
941 ptr_cookie1 = &cookie[len_cookie - len_pattern];
942 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
944 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
947 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
948 ptr_cookie2[1] != '.' ||
949 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
952 version_major = ptr_cookie2[0] - '0';
953 version_minor = ptr_cookie2[2] - '0';
955 return VERSION_IDENT(version_major, version_minor, 0);
958 boolean checkCookieString(const char *cookie, const char *template)
960 const char *pattern = "_FILE_VERSION_?.?";
961 const int len_cookie = strlen(cookie);
962 const int len_template = strlen(template);
963 const int len_pattern = strlen(pattern);
965 if (len_cookie != len_template)
968 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
974 /* ------------------------------------------------------------------------- */
975 /* setup file list handling functions */
976 /* ------------------------------------------------------------------------- */
978 int get_string_integer_value(char *s)
980 static char *number_text[][3] =
982 { "0", "zero", "null", },
983 { "1", "one", "first" },
984 { "2", "two", "second" },
985 { "3", "three", "third" },
986 { "4", "four", "fourth" },
987 { "5", "five", "fifth" },
988 { "6", "six", "sixth" },
989 { "7", "seven", "seventh" },
990 { "8", "eight", "eighth" },
991 { "9", "nine", "ninth" },
992 { "10", "ten", "tenth" },
993 { "11", "eleven", "eleventh" },
994 { "12", "twelve", "twelfth" },
998 char *s_lower = getStringToLower(s);
1001 for (i=0; i<13; i++)
1003 if (strcmp(s_lower, number_text[i][j]) == 0)
1014 boolean get_string_boolean_value(char *s)
1016 char *s_lower = getStringToLower(s);
1017 boolean result = FALSE;
1019 if (strcmp(s_lower, "true") == 0 ||
1020 strcmp(s_lower, "yes") == 0 ||
1021 strcmp(s_lower, "on") == 0 ||
1022 get_string_integer_value(s) == 1)
1030 char *getFormattedSetupEntry(char *token, char *value)
1033 static char entry[MAX_LINE_LEN];
1035 /* start with the token and some spaces to format output line */
1036 sprintf(entry, "%s:", token);
1037 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1040 /* continue with the token's value */
1041 strcat(entry, value);
1046 void freeSetupFileList(struct SetupFileList *setup_file_list)
1048 if (!setup_file_list)
1051 if (setup_file_list->token)
1052 free(setup_file_list->token);
1053 if (setup_file_list->value)
1054 free(setup_file_list->value);
1055 if (setup_file_list->next)
1056 freeSetupFileList(setup_file_list->next);
1057 free(setup_file_list);
1060 static struct SetupFileList *newSetupFileList(char *token, char *value)
1062 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1064 new->token = checked_malloc(strlen(token) + 1);
1065 strcpy(new->token, token);
1067 new->value = checked_malloc(strlen(value) + 1);
1068 strcpy(new->value, value);
1075 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1077 if (!setup_file_list)
1080 if (strcmp(setup_file_list->token, token) == 0)
1081 return setup_file_list->value;
1083 return getTokenValue(setup_file_list->next, token);
1086 static void setTokenValue(struct SetupFileList *setup_file_list,
1087 char *token, char *value)
1089 if (!setup_file_list)
1092 if (strcmp(setup_file_list->token, token) == 0)
1094 free(setup_file_list->value);
1095 setup_file_list->value = checked_malloc(strlen(value) + 1);
1096 strcpy(setup_file_list->value, value);
1098 else if (setup_file_list->next == NULL)
1099 setup_file_list->next = newSetupFileList(token, value);
1101 setTokenValue(setup_file_list->next, token, value);
1105 static void printSetupFileList(struct SetupFileList *setup_file_list)
1107 if (!setup_file_list)
1110 printf("token: '%s'\n", setup_file_list->token);
1111 printf("value: '%s'\n", setup_file_list->value);
1113 printSetupFileList(setup_file_list->next);
1117 struct SetupFileList *loadSetupFileList(char *filename)
1120 char line[MAX_LINE_LEN];
1121 char *token, *value, *line_ptr;
1122 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1123 struct SetupFileList *first_valid_list_entry;
1127 if (!(file = fopen(filename, MODE_READ)))
1129 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1135 /* read next line of input file */
1136 if (!fgets(line, MAX_LINE_LEN, file))
1139 /* cut trailing comment or whitespace from input line */
1140 for (line_ptr = line; *line_ptr; line_ptr++)
1142 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1149 /* cut trailing whitespaces from input line */
1150 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1151 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1154 /* ignore empty lines */
1158 line_len = strlen(line);
1160 /* cut leading whitespaces from token */
1161 for (token = line; *token; token++)
1162 if (*token != ' ' && *token != '\t')
1165 /* find end of token */
1166 for (line_ptr = token; *line_ptr; line_ptr++)
1168 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1175 if (line_ptr < line + line_len)
1176 value = line_ptr + 1;
1180 /* cut leading whitespaces from value */
1181 for (; *value; value++)
1182 if (*value != ' ' && *value != '\t')
1185 if (*token && *value)
1186 setTokenValue(setup_file_list, token, value);
1191 first_valid_list_entry = setup_file_list->next;
1193 /* free empty list header */
1194 setup_file_list->next = NULL;
1195 freeSetupFileList(setup_file_list);
1197 if (first_valid_list_entry == NULL)
1198 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1200 return first_valid_list_entry;
1203 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1206 if (!setup_file_list)
1209 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1211 if (!checkCookieString(setup_file_list->value, identifier))
1213 Error(ERR_WARN, "configuration file has wrong file identifier");
1220 if (setup_file_list->next)
1221 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1224 Error(ERR_WARN, "configuration file has no file identifier");
1230 /* ========================================================================= */
1231 /* setup file stuff */
1232 /* ========================================================================= */
1234 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1235 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1236 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1238 /* level directory info */
1239 #define LEVELINFO_TOKEN_IDENTIFIER 0
1240 #define LEVELINFO_TOKEN_NAME 1
1241 #define LEVELINFO_TOKEN_NAME_SORTING 2
1242 #define LEVELINFO_TOKEN_AUTHOR 3
1243 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1244 #define LEVELINFO_TOKEN_LEVELS 5
1245 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1246 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1247 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1248 #define LEVELINFO_TOKEN_READONLY 9
1249 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1250 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1251 #define LEVELINFO_TOKEN_MUSIC_SET 12
1253 #define NUM_LEVELINFO_TOKENS 13
1255 static LevelDirTree ldi;
1257 static struct TokenInfo levelinfo_tokens[] =
1259 /* level directory info */
1260 { TYPE_STRING, &ldi.identifier, "identifier" },
1261 { TYPE_STRING, &ldi.name, "name" },
1262 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1263 { TYPE_STRING, &ldi.author, "author" },
1264 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1265 { TYPE_INTEGER, &ldi.levels, "levels" },
1266 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1267 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1268 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1269 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1270 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1271 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1272 { TYPE_STRING, &ldi.music_set, "music_set" }
1275 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1279 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1280 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1281 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1282 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1285 ldi->node_parent = NULL;
1286 ldi->node_group = NULL;
1290 ldi->cl_cursor = -1;
1292 ldi->filename = NULL;
1293 ldi->fullpath = NULL;
1294 ldi->basepath = NULL;
1295 ldi->identifier = NULL;
1296 ldi->name = getStringCopy(ANONYMOUS_NAME);
1297 ldi->name_sorting = NULL;
1298 ldi->author = getStringCopy(ANONYMOUS_NAME);
1300 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1301 ldi->parent_link = FALSE;
1302 ldi->user_defined = FALSE;
1304 ldi->class_desc = NULL;
1306 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1308 ldi->imported_from = NULL;
1309 ldi->graphics_set = NULL;
1310 ldi->sounds_set = NULL;
1311 ldi->music_set = NULL;
1312 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1313 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1314 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1316 ldi->first_level = 0;
1317 ldi->last_level = 0;
1318 ldi->level_group = FALSE;
1319 ldi->handicap_level = 0;
1320 ldi->readonly = TRUE;
1324 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1328 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1330 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1334 /* first copy all values from the parent structure ... */
1337 /* ... then set all fields to default that cannot be inherited from parent.
1338 This is especially important for all those fields that can be set from
1339 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1340 calls 'free()' for all already set token values which requires that no
1341 other structure's pointer may point to them!
1344 ldi->filename = NULL;
1345 ldi->fullpath = NULL;
1346 ldi->basepath = NULL;
1347 ldi->identifier = NULL;
1348 ldi->name = getStringCopy(ANONYMOUS_NAME);
1349 ldi->name_sorting = NULL;
1350 ldi->author = getStringCopy(parent->author);
1351 ldi->imported_from = getStringCopy(parent->imported_from);
1353 ldi->level_group = FALSE;
1354 ldi->parent_link = FALSE;
1356 ldi->node_top = parent->node_top;
1357 ldi->node_parent = parent;
1358 ldi->node_group = NULL;
1362 void setSetupInfo(struct TokenInfo *token_info,
1363 int token_nr, char *token_value)
1365 int token_type = token_info[token_nr].type;
1366 void *setup_value = token_info[token_nr].value;
1368 if (token_value == NULL)
1371 /* set setup field to corresponding token value */
1376 *(boolean *)setup_value = get_string_boolean_value(token_value);
1380 *(Key *)setup_value = getKeyFromKeyName(token_value);
1384 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1388 *(int *)setup_value = get_string_integer_value(token_value);
1392 if (*(char **)setup_value != NULL)
1393 free(*(char **)setup_value);
1394 *(char **)setup_value = getStringCopy(token_value);
1402 static int compareTreeInfoEntries(const void *object1, const void *object2)
1404 const TreeInfo *entry1 = *((TreeInfo **)object1);
1405 const TreeInfo *entry2 = *((TreeInfo **)object2);
1406 int class_sorting1, class_sorting2;
1409 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1411 class_sorting1 = LEVELSORTING(entry1);
1412 class_sorting2 = LEVELSORTING(entry2);
1416 class_sorting1 = ARTWORKSORTING(entry1);
1417 class_sorting2 = ARTWORKSORTING(entry2);
1420 if (entry1->parent_link || entry2->parent_link)
1421 compare_result = (entry1->parent_link ? -1 : +1);
1422 else if (entry1->sort_priority == entry2->sort_priority)
1424 char *name1 = getStringToLower(entry1->name_sorting);
1425 char *name2 = getStringToLower(entry2->name_sorting);
1427 compare_result = strcmp(name1, name2);
1432 else if (class_sorting1 == class_sorting2)
1433 compare_result = entry1->sort_priority - entry2->sort_priority;
1435 compare_result = class_sorting1 - class_sorting2;
1437 return compare_result;
1440 static void createParentTreeInfoNode(TreeInfo *node_parent)
1444 if (node_parent == NULL)
1447 ti_new = newTreeInfo();
1448 setTreeInfoToDefaults(ti_new, node_parent->type);
1450 ti_new->node_parent = node_parent;
1451 ti_new->parent_link = TRUE;
1453 ti_new->identifier = getStringCopy(node_parent->identifier);
1454 ti_new->name = ".. (parent directory)";
1455 ti_new->name_sorting = getStringCopy(ti_new->name);
1457 ti_new->filename = "..";
1458 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1460 ti_new->sort_priority = node_parent->sort_priority;
1461 ti_new->class_desc = getLevelClassDescription(ti_new);
1463 pushTreeInfo(&node_parent->node_group, ti_new);
1466 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1467 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1469 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1470 TreeInfo *node_parent,
1471 char *level_directory,
1472 char *directory_name)
1474 char *directory_path = getPath2(level_directory, directory_name);
1475 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1476 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1477 LevelDirTree *leveldir_new = NULL;
1480 if (setup_file_list == NULL)
1482 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1484 free(directory_path);
1490 leveldir_new = newTreeInfo();
1493 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1495 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1497 leveldir_new->filename = getStringCopy(directory_name);
1499 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1501 /* set all structure fields according to the token/value pairs */
1502 ldi = *leveldir_new;
1503 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1504 setSetupInfo(levelinfo_tokens, i,
1505 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1506 *leveldir_new = ldi;
1508 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1510 free(leveldir_new->name);
1511 leveldir_new->name = getStringCopy(leveldir_new->filename);
1514 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1516 if (leveldir_new->identifier == NULL)
1517 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1519 if (leveldir_new->name_sorting == NULL)
1520 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1522 if (node_parent == NULL) /* top level group */
1524 leveldir_new->basepath = level_directory;
1525 leveldir_new->fullpath = leveldir_new->filename;
1527 else /* sub level group */
1529 leveldir_new->basepath = node_parent->basepath;
1530 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1533 if (leveldir_new->levels < 1)
1534 leveldir_new->levels = 1;
1536 leveldir_new->last_level =
1537 leveldir_new->first_level + leveldir_new->levels - 1;
1539 leveldir_new->user_defined =
1540 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1542 leveldir_new->color = LEVELCOLOR(leveldir_new);
1543 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1545 leveldir_new->handicap_level = /* set handicap to default value */
1546 (leveldir_new->user_defined ?
1547 leveldir_new->last_level :
1548 leveldir_new->first_level);
1550 pushTreeInfo(node_first, leveldir_new);
1552 freeSetupFileList(setup_file_list);
1554 if (leveldir_new->level_group)
1556 /* create node to link back to current level directory */
1557 createParentTreeInfoNode(leveldir_new);
1559 /* step into sub-directory and look for more level series */
1560 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1561 leveldir_new, directory_path);
1564 free(directory_path);
1570 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1571 TreeInfo *node_parent,
1572 char *level_directory)
1575 struct dirent *dir_entry;
1576 boolean valid_entry_found = FALSE;
1578 if ((dir = opendir(level_directory)) == NULL)
1580 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1584 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1586 struct stat file_status;
1587 char *directory_name = dir_entry->d_name;
1588 char *directory_path = getPath2(level_directory, directory_name);
1590 /* skip entries for current and parent directory */
1591 if (strcmp(directory_name, ".") == 0 ||
1592 strcmp(directory_name, "..") == 0)
1594 free(directory_path);
1598 /* find out if directory entry is itself a directory */
1599 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1600 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1602 free(directory_path);
1606 free(directory_path);
1608 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1609 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1610 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1613 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1620 if (!valid_entry_found)
1622 /* check if this directory directly contains a file "levelinfo.conf" */
1623 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1624 level_directory, ".");
1627 if (!valid_entry_found)
1628 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1632 void LoadLevelInfo()
1634 InitUserLevelDirectory(getLoginName());
1636 DrawInitText("Loading level series:", 120, FC_GREEN);
1638 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1639 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1641 /* before sorting, the first entries will be from the user directory */
1642 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1644 if (leveldir_first == NULL)
1645 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1647 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1650 dumpTreeInfo(leveldir_first, 0);
1654 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1655 TreeInfo *node_parent,
1656 char *base_directory,
1657 char *directory_name, int type)
1659 char *directory_path = getPath2(base_directory, directory_name);
1660 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1661 struct SetupFileList *setup_file_list = NULL;
1662 TreeInfo *artwork_new = NULL;
1665 if (access(filename, F_OK) == 0) /* file exists */
1666 setup_file_list = loadSetupFileList(filename);
1668 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1671 struct dirent *dir_entry;
1672 boolean valid_file_found = FALSE;
1674 if ((dir = opendir(directory_path)) != NULL)
1676 while ((dir_entry = readdir(dir)) != NULL)
1678 char *entry_name = dir_entry->d_name;
1680 if (FileIsArtworkType(entry_name, type))
1682 valid_file_found = TRUE;
1690 if (!valid_file_found)
1692 if (strcmp(directory_name, ".") != 0)
1693 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1695 free(directory_path);
1702 artwork_new = newTreeInfo();
1705 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1707 setTreeInfoToDefaults(artwork_new, type);
1709 artwork_new->filename = getStringCopy(directory_name);
1711 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1714 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1717 /* set all structure fields according to the token/value pairs */
1719 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1720 setSetupInfo(levelinfo_tokens, i,
1721 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1724 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1726 free(artwork_new->name);
1727 artwork_new->name = getStringCopy(artwork_new->filename);
1731 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1734 if (artwork_new->identifier == NULL)
1735 artwork_new->identifier = getStringCopy(artwork_new->filename);
1737 if (artwork_new->name_sorting == NULL)
1738 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1741 if (node_parent == NULL) /* top level group */
1743 artwork_new->basepath = getStringCopy(base_directory);
1744 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1746 else /* sub level group */
1748 artwork_new->basepath = getStringCopy(node_parent->basepath);
1749 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1752 artwork_new->user_defined =
1753 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1755 /* (may use ".sort_priority" from "setup_file_list" above) */
1756 artwork_new->color = ARTWORKCOLOR(artwork_new);
1757 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1759 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1761 if (artwork_new->name != NULL)
1762 free(artwork_new->name);
1764 if (strcmp(artwork_new->filename, ".") == 0)
1766 if (artwork_new->user_defined)
1768 artwork_new->identifier = getStringCopy("private");
1769 artwork_new->sort_priority = ARTWORKCLASS_USER;
1773 artwork_new->identifier = getStringCopy("classic");
1774 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1777 /* set to new values after changing ".sort_priority" */
1778 artwork_new->color = ARTWORKCOLOR(artwork_new);
1779 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1783 artwork_new->identifier = getStringCopy(artwork_new->filename);
1786 artwork_new->name = getStringCopy(artwork_new->identifier);
1787 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1790 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1792 pushTreeInfo(node_first, artwork_new);
1794 freeSetupFileList(setup_file_list);
1796 free(directory_path);
1802 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1803 TreeInfo *node_parent,
1804 char *base_directory, int type)
1807 struct dirent *dir_entry;
1808 boolean valid_entry_found = FALSE;
1810 if ((dir = opendir(base_directory)) == NULL)
1812 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1813 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1817 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1819 struct stat file_status;
1820 char *directory_name = dir_entry->d_name;
1821 char *directory_path = getPath2(base_directory, directory_name);
1823 /* skip entries for current and parent directory */
1824 if (strcmp(directory_name, ".") == 0 ||
1825 strcmp(directory_name, "..") == 0)
1827 free(directory_path);
1831 /* find out if directory entry is itself a directory */
1832 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1833 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1835 free(directory_path);
1839 free(directory_path);
1841 /* check if this directory contains artwork with or without config file */
1842 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1844 directory_name, type);
1849 /* check if this directory directly contains artwork itself */
1850 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1851 base_directory, ".",
1853 if (!valid_entry_found)
1854 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1858 static TreeInfo *getDummyArtworkInfo(int type)
1860 /* this is only needed when there is completely no artwork available */
1861 TreeInfo *artwork_new = newTreeInfo();
1863 setTreeInfoToDefaults(artwork_new, type);
1865 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1866 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1867 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1869 if (artwork_new->name != NULL)
1870 free(artwork_new->name);
1872 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1873 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1874 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1879 void LoadArtworkInfo()
1881 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1883 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1884 options.graphics_directory,
1885 TREE_TYPE_GRAPHICS_DIR);
1886 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1887 getUserGraphicsDir(),
1888 TREE_TYPE_GRAPHICS_DIR);
1890 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1891 options.sounds_directory,
1892 TREE_TYPE_SOUNDS_DIR);
1893 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1895 TREE_TYPE_SOUNDS_DIR);
1897 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1898 options.music_directory,
1899 TREE_TYPE_MUSIC_DIR);
1900 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1902 TREE_TYPE_MUSIC_DIR);
1904 if (artwork.gfx_first == NULL)
1905 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1906 if (artwork.snd_first == NULL)
1907 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1908 if (artwork.mus_first == NULL)
1909 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1911 /* before sorting, the first entries will be from the user directory */
1912 artwork.gfx_current =
1913 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1914 if (artwork.gfx_current == NULL)
1915 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1917 artwork.snd_current =
1918 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1919 if (artwork.snd_current == NULL)
1920 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1922 artwork.mus_current =
1923 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1924 if (artwork.mus_current == NULL)
1925 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1927 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1928 artwork.snd_current_identifier = artwork.snd_current->identifier;
1929 artwork.mus_current_identifier = artwork.mus_current->identifier;
1932 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1933 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1934 printf("music set == %s\n\n", artwork.mus_current_identifier);
1937 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1938 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1939 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1942 dumpTreeInfo(artwork.gfx_first, 0);
1943 dumpTreeInfo(artwork.snd_first, 0);
1944 dumpTreeInfo(artwork.mus_first, 0);
1948 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1949 LevelDirTree *level_node)
1951 /* recursively check all level directories for artwork sub-directories */
1955 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1956 ARTWORK_DIRECTORY((*artwork_node)->type));
1959 if (!level_node->parent_link)
1960 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1961 level_node->filename, level_node->name);
1964 if (!level_node->parent_link)
1966 TreeInfo *topnode_last = *artwork_node;
1968 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1969 (*artwork_node)->type);
1971 if (topnode_last != *artwork_node)
1973 free((*artwork_node)->identifier);
1974 free((*artwork_node)->name);
1975 free((*artwork_node)->name_sorting);
1977 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1978 (*artwork_node)->name = getStringCopy(level_node->name);
1979 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1981 (*artwork_node)->sort_priority = level_node->sort_priority;
1982 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1988 if (level_node->node_group != NULL)
1989 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1991 level_node = level_node->next;
1995 void LoadLevelArtworkInfo()
1997 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1999 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2000 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2001 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2003 /* needed for reloading level artwork not known at ealier stage */
2004 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2006 artwork.gfx_current =
2007 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2008 if (artwork.gfx_current == NULL)
2009 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2012 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2014 artwork.snd_current =
2015 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2016 if (artwork.snd_current == NULL)
2017 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2020 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2022 artwork.mus_current =
2023 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2024 if (artwork.mus_current == NULL)
2025 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2028 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2029 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2030 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2033 dumpTreeInfo(artwork.gfx_first, 0);
2034 dumpTreeInfo(artwork.snd_first, 0);
2035 dumpTreeInfo(artwork.mus_first, 0);
2039 static void SaveUserLevelInfo()
2045 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2047 if (!(file = fopen(filename, MODE_WRITE)))
2049 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2054 /* always start with reliable default values */
2055 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2057 ldi.name = getStringCopy(getLoginName());
2058 ldi.author = getStringCopy(getRealName());
2060 ldi.first_level = 1;
2061 ldi.sort_priority = LEVELCLASS_USER_START;
2062 ldi.readonly = FALSE;
2064 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2065 getCookie("LEVELINFO")));
2067 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2068 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2069 i != LEVELINFO_TOKEN_NAME_SORTING &&
2070 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2071 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2076 SetFilePermissions(filename, PERMS_PRIVATE);
2079 char *getSetupValue(int type, void *value)
2081 static char value_string[MAX_LINE_LEN];
2089 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2093 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2097 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2101 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2105 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2109 sprintf(value_string, "%d", *(int *)value);
2113 strcpy(value_string, *(char **)value);
2117 value_string[0] = '\0';
2121 return value_string;
2124 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2128 static char token_string[MAX_LINE_LEN];
2129 int token_type = token_info[token_nr].type;
2130 void *setup_value = token_info[token_nr].value;
2131 char *token_text = token_info[token_nr].text;
2132 char *value_string = getSetupValue(token_type, setup_value);
2134 /* build complete token string */
2135 sprintf(token_string, "%s%s", prefix, token_text);
2137 /* build setup entry line */
2138 line = getFormattedSetupEntry(token_string, value_string);
2140 if (token_type == TYPE_KEY_X11)
2142 Key key = *(Key *)setup_value;
2143 char *keyname = getKeyNameFromKey(key);
2145 /* add comment, if useful */
2146 if (strcmp(keyname, "(undefined)") != 0 &&
2147 strcmp(keyname, "(unknown)") != 0)
2149 /* add at least one whitespace */
2151 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2155 strcat(line, keyname);
2162 void LoadLevelSetup_LastSeries()
2165 struct SetupFileList *level_setup_list = NULL;
2167 /* always start with reliable default values */
2168 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2170 /* ----------------------------------------------------------------------- */
2171 /* ~/.<program>/levelsetup.conf */
2172 /* ----------------------------------------------------------------------- */
2174 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2176 if ((level_setup_list = loadSetupFileList(filename)))
2178 char *last_level_series =
2179 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2181 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2183 if (leveldir_current == NULL)
2184 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2186 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2188 freeSetupFileList(level_setup_list);
2191 Error(ERR_WARN, "using default setup values");
2196 void SaveLevelSetup_LastSeries()
2199 char *level_subdir = leveldir_current->filename;
2202 /* ----------------------------------------------------------------------- */
2203 /* ~/.<program>/levelsetup.conf */
2204 /* ----------------------------------------------------------------------- */
2206 InitUserDataDirectory();
2208 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2210 if (!(file = fopen(filename, MODE_WRITE)))
2212 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2217 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2218 getCookie("LEVELSETUP")));
2219 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2225 SetFilePermissions(filename, PERMS_PRIVATE);
2228 static void checkSeriesInfo()
2230 static char *level_directory = NULL;
2232 struct dirent *dir_entry;
2234 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2236 level_directory = getPath2((leveldir_current->user_defined ?
2237 getUserLevelDir(NULL) :
2238 options.level_directory),
2239 leveldir_current->fullpath);
2241 if ((dir = opendir(level_directory)) == NULL)
2243 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2247 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2249 if (strlen(dir_entry->d_name) > 4 &&
2250 dir_entry->d_name[3] == '.' &&
2251 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2253 char levelnum_str[4];
2256 strncpy(levelnum_str, dir_entry->d_name, 3);
2257 levelnum_str[3] = '\0';
2259 levelnum_value = atoi(levelnum_str);
2261 if (levelnum_value < leveldir_current->first_level)
2263 Error(ERR_WARN, "additional level %d found", levelnum_value);
2264 leveldir_current->first_level = levelnum_value;
2266 else if (levelnum_value > leveldir_current->last_level)
2268 Error(ERR_WARN, "additional level %d found", levelnum_value);
2269 leveldir_current->last_level = levelnum_value;
2277 void LoadLevelSetup_SeriesInfo()
2280 struct SetupFileList *level_setup_list = NULL;
2281 char *level_subdir = leveldir_current->filename;
2283 /* always start with reliable default values */
2284 level_nr = leveldir_current->first_level;
2286 checkSeriesInfo(leveldir_current);
2288 /* ----------------------------------------------------------------------- */
2289 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2290 /* ----------------------------------------------------------------------- */
2292 level_subdir = leveldir_current->filename;
2294 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2296 if ((level_setup_list = loadSetupFileList(filename)))
2300 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2304 level_nr = atoi(token_value);
2306 if (level_nr < leveldir_current->first_level)
2307 level_nr = leveldir_current->first_level;
2308 if (level_nr > leveldir_current->last_level)
2309 level_nr = leveldir_current->last_level;
2312 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2316 int level_nr = atoi(token_value);
2318 if (level_nr < leveldir_current->first_level)
2319 level_nr = leveldir_current->first_level;
2320 if (level_nr > leveldir_current->last_level + 1)
2321 level_nr = leveldir_current->last_level;
2323 if (leveldir_current->user_defined)
2324 level_nr = leveldir_current->last_level;
2326 leveldir_current->handicap_level = level_nr;
2329 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2331 freeSetupFileList(level_setup_list);
2334 Error(ERR_WARN, "using default setup values");
2339 void SaveLevelSetup_SeriesInfo()
2342 char *level_subdir = leveldir_current->filename;
2343 char *level_nr_str = int2str(level_nr, 0);
2344 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2347 /* ----------------------------------------------------------------------- */
2348 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2349 /* ----------------------------------------------------------------------- */
2351 InitLevelSetupDirectory(level_subdir);
2353 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2355 if (!(file = fopen(filename, MODE_WRITE)))
2357 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2362 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2363 getCookie("LEVELSETUP")));
2364 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2366 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2367 handicap_level_str));
2372 SetFilePermissions(filename, PERMS_PRIVATE);