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 char *getLevelFilename(int nr)
302 static char *filename = NULL;
303 char basename[MAX_FILENAME_LEN];
305 if (filename != NULL)
308 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
309 filename = getPath2(getCurrentLevelDir(), basename);
314 char *getTapeFilename(int nr)
316 static char *filename = NULL;
317 char basename[MAX_FILENAME_LEN];
319 if (filename != NULL)
322 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
323 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
328 char *getScoreFilename(int nr)
330 static char *filename = NULL;
331 char basename[MAX_FILENAME_LEN];
333 if (filename != NULL)
336 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
337 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
342 char *getSetupFilename()
344 static char *filename = NULL;
346 if (filename != NULL)
349 filename = getPath2(getSetupDir(), SETUP_FILENAME);
354 static char *getSetupArtworkDir(TreeInfo *ti)
356 static char *artwork_dir = NULL;
358 if (artwork_dir != NULL)
361 artwork_dir = getPath2(ti->basepath, ti->fullpath);
366 static char *getCorrectedImageBasename(char *basename)
368 char *result = basename;
370 #if defined(PLATFORM_MSDOS)
371 if (program.filename_prefix != NULL)
373 int prefix_len = strlen(program.filename_prefix);
375 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
376 result = &basename[prefix_len];
383 static boolean fileExists(char *filename)
386 printf("checking file '%s'\n", filename);
389 return (access(filename, F_OK) == 0);
392 char *getCustomImageFilename(char *basename)
394 static char *filename = NULL;
396 if (filename != NULL)
399 basename = getCorrectedImageBasename(basename);
401 if (!setup.override_level_graphics)
403 /* 1st try: look for special artwork in current level series directory */
404 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
405 if (fileExists(filename))
409 /* 2nd try: look for special artwork in configured artwork directory */
410 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
411 if (fileExists(filename))
414 /* 3rd try: look for default artwork in new default artwork directory */
415 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
416 if (fileExists(filename))
419 /* 4th try: look for default artwork in old default artwork directory */
420 filename = getPath2(options.graphics_directory, basename);
421 if (fileExists(filename))
424 return NULL; /* cannot find specified artwork file anywhere */
427 char *getCustomSoundFilename(char *basename)
429 static char *filename = NULL;
431 if (filename != NULL)
434 if (!setup.override_level_sounds)
436 /* 1st try: look for special artwork in current level series directory */
437 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
438 if (fileExists(filename))
442 /* 2nd try: look for special artwork in configured artwork directory */
443 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
444 if (fileExists(filename))
447 /* 3rd try: look for default artwork in new default artwork directory */
448 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
449 if (fileExists(filename))
452 /* 4th try: look for default artwork in old default artwork directory */
453 filename = getPath2(options.sounds_directory, basename);
454 if (fileExists(filename))
457 return NULL; /* cannot find specified artwork file anywhere */
460 char *getCustomSoundConfigFilename()
462 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
465 char *getCustomMusicDirectory(void)
467 static char *directory = NULL;
469 if (directory != NULL)
472 if (!setup.override_level_music)
474 /* 1st try: look for special artwork in current level series directory */
475 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
476 if (fileExists(directory))
480 /* 2nd try: look for special artwork in configured artwork directory */
481 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
482 if (fileExists(directory))
485 /* 3rd try: look for default artwork in new default artwork directory */
486 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
487 if (fileExists(directory))
490 /* 4th try: look for default artwork in old default artwork directory */
491 directory = getStringCopy(options.music_directory);
492 if (fileExists(directory))
495 return NULL; /* cannot find specified artwork file anywhere */
498 void InitTapeDirectory(char *level_subdir)
500 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
501 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
502 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
505 void InitScoreDirectory(char *level_subdir)
507 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
508 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
511 static void SaveUserLevelInfo();
513 void InitUserLevelDirectory(char *level_subdir)
515 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
517 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
518 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
519 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
525 void InitLevelSetupDirectory(char *level_subdir)
527 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
528 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
529 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
533 /* ------------------------------------------------------------------------- */
534 /* some functions to handle lists of level directories */
535 /* ------------------------------------------------------------------------- */
537 TreeInfo *newTreeInfo()
539 return checked_calloc(sizeof(TreeInfo));
542 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
544 node_new->next = *node_first;
545 *node_first = node_new;
548 int numTreeInfo(TreeInfo *node)
561 boolean validLevelSeries(TreeInfo *node)
563 return (node != NULL && !node->node_group && !node->parent_link);
566 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
571 if (node->node_group) /* enter level group (step down into tree) */
572 return getFirstValidTreeInfoEntry(node->node_group);
573 else if (node->parent_link) /* skip start entry of level group */
575 if (node->next) /* get first real level series entry */
576 return getFirstValidTreeInfoEntry(node->next);
577 else /* leave empty level group and go on */
578 return getFirstValidTreeInfoEntry(node->node_parent->next);
580 else /* this seems to be a regular level series */
584 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
589 if (node->node_parent == NULL) /* top level group */
590 return *node->node_top;
591 else /* sub level group */
592 return node->node_parent->node_group;
595 int numTreeInfoInGroup(TreeInfo *node)
597 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
600 int posTreeInfo(TreeInfo *node)
602 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
607 if (node_cmp == node)
611 node_cmp = node_cmp->next;
617 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
619 TreeInfo *node_default = node;
634 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
636 if (identifier == NULL)
641 if (node->node_group)
643 TreeInfo *node_group;
645 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
650 else if (!node->parent_link)
652 if (strcmp(identifier, node->identifier) == 0)
662 void dumpTreeInfo(TreeInfo *node, int depth)
666 printf("Dumping TreeInfo:\n");
670 for (i=0; i<(depth + 1) * 3; i++)
673 printf("filename == '%s' (%s) [%s] (%d)\n",
674 node->filename, node->name, node->identifier, node->sort_priority);
676 if (node->node_group != NULL)
677 dumpTreeInfo(node->node_group, depth + 1);
683 void sortTreeInfo(TreeInfo **node_first,
684 int (*compare_function)(const void *, const void *))
686 int num_nodes = numTreeInfo(*node_first);
687 TreeInfo **sort_array;
688 TreeInfo *node = *node_first;
694 /* allocate array for sorting structure pointers */
695 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
697 /* writing structure pointers to sorting array */
698 while (i < num_nodes && node) /* double boundary check... */
700 sort_array[i] = node;
706 /* sorting the structure pointers in the sorting array */
707 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
710 /* update the linkage of list elements with the sorted node array */
711 for (i=0; i<num_nodes - 1; i++)
712 sort_array[i]->next = sort_array[i + 1];
713 sort_array[num_nodes - 1]->next = NULL;
715 /* update the linkage of the main list anchor pointer */
716 *node_first = sort_array[0];
720 /* now recursively sort the level group structures */
724 if (node->node_group != NULL)
725 sortTreeInfo(&node->node_group, compare_function);
732 /* ========================================================================= */
733 /* some stuff from "files.c" */
734 /* ========================================================================= */
736 #if defined(PLATFORM_WIN32)
738 #define S_IRGRP S_IRUSR
741 #define S_IROTH S_IRUSR
744 #define S_IWGRP S_IWUSR
747 #define S_IWOTH S_IWUSR
750 #define S_IXGRP S_IXUSR
753 #define S_IXOTH S_IXUSR
756 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
761 #endif /* PLATFORM_WIN32 */
763 /* file permissions for newly written files */
764 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
765 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
766 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
768 #define MODE_W_PRIVATE (S_IWUSR)
769 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
770 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
772 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
773 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
775 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
776 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
778 char *getUserDataDir(void)
780 static char *userdata_dir = NULL;
784 char *home_dir = getHomeDir();
785 char *data_dir = program.userdata_directory;
787 userdata_dir = getPath2(home_dir, data_dir);
795 return getUserDataDir();
798 static mode_t posix_umask(mode_t mask)
800 #if defined(PLATFORM_UNIX)
807 static int posix_mkdir(const char *pathname, mode_t mode)
809 #if defined(PLATFORM_WIN32)
810 return mkdir(pathname);
812 return mkdir(pathname, mode);
816 void createDirectory(char *dir, char *text, int permission_class)
818 /* leave "other" permissions in umask untouched, but ensure group parts
819 of USERDATA_DIR_MODE are not masked */
820 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
821 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
822 mode_t normal_umask = posix_umask(0);
823 mode_t group_umask = ~(dir_mode & S_IRWXG);
824 posix_umask(normal_umask & group_umask);
826 if (access(dir, F_OK) != 0)
827 if (posix_mkdir(dir, dir_mode) != 0)
828 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
830 posix_umask(normal_umask); /* reset normal umask */
833 void InitUserDataDirectory()
835 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
838 void SetFilePermissions(char *filename, int permission_class)
840 chmod(filename, (permission_class == PERMS_PRIVATE ?
841 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
844 char *getCookie(char *file_type)
846 static char cookie[MAX_COOKIE_LEN + 1];
848 if (strlen(program.cookie_prefix) + 1 +
849 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
850 return "[COOKIE ERROR]"; /* should never happen */
852 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
853 program.cookie_prefix, file_type,
854 program.version_major, program.version_minor);
859 int getFileVersionFromCookieString(const char *cookie)
861 const char *ptr_cookie1, *ptr_cookie2;
862 const char *pattern1 = "_FILE_VERSION_";
863 const char *pattern2 = "?.?";
864 const int len_cookie = strlen(cookie);
865 const int len_pattern1 = strlen(pattern1);
866 const int len_pattern2 = strlen(pattern2);
867 const int len_pattern = len_pattern1 + len_pattern2;
868 int version_major, version_minor;
870 if (len_cookie <= len_pattern)
873 ptr_cookie1 = &cookie[len_cookie - len_pattern];
874 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
876 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
879 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
880 ptr_cookie2[1] != '.' ||
881 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
884 version_major = ptr_cookie2[0] - '0';
885 version_minor = ptr_cookie2[2] - '0';
887 return VERSION_IDENT(version_major, version_minor, 0);
890 boolean checkCookieString(const char *cookie, const char *template)
892 const char *pattern = "_FILE_VERSION_?.?";
893 const int len_cookie = strlen(cookie);
894 const int len_template = strlen(template);
895 const int len_pattern = strlen(pattern);
897 if (len_cookie != len_template)
900 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
906 /* ------------------------------------------------------------------------- */
907 /* setup file list handling functions */
908 /* ------------------------------------------------------------------------- */
910 int get_string_integer_value(char *s)
912 static char *number_text[][3] =
914 { "0", "zero", "null", },
915 { "1", "one", "first" },
916 { "2", "two", "second" },
917 { "3", "three", "third" },
918 { "4", "four", "fourth" },
919 { "5", "five", "fifth" },
920 { "6", "six", "sixth" },
921 { "7", "seven", "seventh" },
922 { "8", "eight", "eighth" },
923 { "9", "nine", "ninth" },
924 { "10", "ten", "tenth" },
925 { "11", "eleven", "eleventh" },
926 { "12", "twelve", "twelfth" },
930 char *s_lower = getStringToLower(s);
935 if (strcmp(s_lower, number_text[i][j]) == 0)
946 boolean get_string_boolean_value(char *s)
948 char *s_lower = getStringToLower(s);
949 boolean result = FALSE;
951 if (strcmp(s_lower, "true") == 0 ||
952 strcmp(s_lower, "yes") == 0 ||
953 strcmp(s_lower, "on") == 0 ||
954 get_string_integer_value(s) == 1)
962 char *getFormattedSetupEntry(char *token, char *value)
965 static char entry[MAX_LINE_LEN];
967 /* start with the token and some spaces to format output line */
968 sprintf(entry, "%s:", token);
969 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
972 /* continue with the token's value */
973 strcat(entry, value);
978 void freeSetupFileList(struct SetupFileList *setup_file_list)
980 if (!setup_file_list)
983 if (setup_file_list->token)
984 free(setup_file_list->token);
985 if (setup_file_list->value)
986 free(setup_file_list->value);
987 if (setup_file_list->next)
988 freeSetupFileList(setup_file_list->next);
989 free(setup_file_list);
992 static struct SetupFileList *newSetupFileList(char *token, char *value)
994 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
996 new->token = checked_malloc(strlen(token) + 1);
997 strcpy(new->token, token);
999 new->value = checked_malloc(strlen(value) + 1);
1000 strcpy(new->value, value);
1007 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1009 if (!setup_file_list)
1012 if (strcmp(setup_file_list->token, token) == 0)
1013 return setup_file_list->value;
1015 return getTokenValue(setup_file_list->next, token);
1018 static void setTokenValue(struct SetupFileList *setup_file_list,
1019 char *token, char *value)
1021 if (!setup_file_list)
1024 if (strcmp(setup_file_list->token, token) == 0)
1026 free(setup_file_list->value);
1027 setup_file_list->value = checked_malloc(strlen(value) + 1);
1028 strcpy(setup_file_list->value, value);
1030 else if (setup_file_list->next == NULL)
1031 setup_file_list->next = newSetupFileList(token, value);
1033 setTokenValue(setup_file_list->next, token, value);
1037 static void printSetupFileList(struct SetupFileList *setup_file_list)
1039 if (!setup_file_list)
1042 printf("token: '%s'\n", setup_file_list->token);
1043 printf("value: '%s'\n", setup_file_list->value);
1045 printSetupFileList(setup_file_list->next);
1049 struct SetupFileList *loadSetupFileList(char *filename)
1052 char line[MAX_LINE_LEN];
1053 char *token, *value, *line_ptr;
1054 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1055 struct SetupFileList *first_valid_list_entry;
1059 if (!(file = fopen(filename, MODE_READ)))
1061 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1067 /* read next line of input file */
1068 if (!fgets(line, MAX_LINE_LEN, file))
1071 /* cut trailing comment or whitespace from input line */
1072 for (line_ptr = line; *line_ptr; line_ptr++)
1074 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1081 /* cut trailing whitespaces from input line */
1082 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1083 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1086 /* ignore empty lines */
1090 line_len = strlen(line);
1092 /* cut leading whitespaces from token */
1093 for (token = line; *token; token++)
1094 if (*token != ' ' && *token != '\t')
1097 /* find end of token */
1098 for (line_ptr = token; *line_ptr; line_ptr++)
1100 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1107 if (line_ptr < line + line_len)
1108 value = line_ptr + 1;
1112 /* cut leading whitespaces from value */
1113 for (; *value; value++)
1114 if (*value != ' ' && *value != '\t')
1117 if (*token && *value)
1118 setTokenValue(setup_file_list, token, value);
1123 first_valid_list_entry = setup_file_list->next;
1125 /* free empty list header */
1126 setup_file_list->next = NULL;
1127 freeSetupFileList(setup_file_list);
1129 if (first_valid_list_entry == NULL)
1130 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1132 return first_valid_list_entry;
1135 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1138 if (!setup_file_list)
1141 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1143 if (!checkCookieString(setup_file_list->value, identifier))
1145 Error(ERR_WARN, "configuration file has wrong file identifier");
1152 if (setup_file_list->next)
1153 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1156 Error(ERR_WARN, "configuration file has no file identifier");
1162 /* ========================================================================= */
1163 /* setup file stuff */
1164 /* ========================================================================= */
1166 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1167 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1168 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1170 /* level directory info */
1171 #define LEVELINFO_TOKEN_IDENTIFIER 0
1172 #define LEVELINFO_TOKEN_NAME 1
1173 #define LEVELINFO_TOKEN_NAME_SORTING 2
1174 #define LEVELINFO_TOKEN_AUTHOR 3
1175 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1176 #define LEVELINFO_TOKEN_LEVELS 5
1177 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1178 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1179 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1180 #define LEVELINFO_TOKEN_READONLY 9
1182 #define NUM_LEVELINFO_TOKENS 10
1184 static LevelDirTree ldi;
1186 static struct TokenInfo levelinfo_tokens[] =
1188 /* level directory info */
1189 { TYPE_STRING, &ldi.identifier, "identifier" },
1190 { TYPE_STRING, &ldi.name, "name" },
1191 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1192 { TYPE_STRING, &ldi.author, "author" },
1193 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1194 { TYPE_INTEGER, &ldi.levels, "levels" },
1195 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1196 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1197 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1198 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1201 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1205 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1206 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1207 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1208 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1211 ldi->node_parent = NULL;
1212 ldi->node_group = NULL;
1216 ldi->cl_cursor = -1;
1218 ldi->filename = NULL;
1219 ldi->fullpath = NULL;
1220 ldi->basepath = NULL;
1221 ldi->identifier = NULL;
1222 ldi->name = getStringCopy(ANONYMOUS_NAME);
1223 ldi->name_sorting = NULL;
1224 ldi->author = getStringCopy(ANONYMOUS_NAME);
1226 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1227 ldi->parent_link = FALSE;
1228 ldi->user_defined = FALSE;
1230 ldi->class_desc = NULL;
1232 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1234 ldi->imported_from = NULL;
1236 ldi->first_level = 0;
1237 ldi->last_level = 0;
1238 ldi->level_group = FALSE;
1239 ldi->handicap_level = 0;
1240 ldi->readonly = TRUE;
1244 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1248 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1250 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1254 /* first copy all values from the parent structure ... */
1257 /* ... then set all fields to default that cannot be inherited from parent.
1258 This is especially important for all those fields that can be set from
1259 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1260 calls 'free()' for all already set token values which requires that no
1261 other structure's pointer may point to them!
1264 ldi->filename = NULL;
1265 ldi->fullpath = NULL;
1266 ldi->basepath = NULL;
1267 ldi->identifier = NULL;
1268 ldi->name = getStringCopy(ANONYMOUS_NAME);
1269 ldi->name_sorting = NULL;
1270 ldi->author = getStringCopy(parent->author);
1271 ldi->imported_from = getStringCopy(parent->imported_from);
1273 ldi->level_group = FALSE;
1274 ldi->parent_link = FALSE;
1276 ldi->node_top = parent->node_top;
1277 ldi->node_parent = parent;
1278 ldi->node_group = NULL;
1282 void setSetupInfo(struct TokenInfo *token_info,
1283 int token_nr, char *token_value)
1285 int token_type = token_info[token_nr].type;
1286 void *setup_value = token_info[token_nr].value;
1288 if (token_value == NULL)
1291 /* set setup field to corresponding token value */
1296 *(boolean *)setup_value = get_string_boolean_value(token_value);
1300 *(Key *)setup_value = getKeyFromKeyName(token_value);
1304 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1308 *(int *)setup_value = get_string_integer_value(token_value);
1312 if (*(char **)setup_value != NULL)
1313 free(*(char **)setup_value);
1314 *(char **)setup_value = getStringCopy(token_value);
1322 static int compareTreeInfoEntries(const void *object1, const void *object2)
1324 const TreeInfo *entry1 = *((TreeInfo **)object1);
1325 const TreeInfo *entry2 = *((TreeInfo **)object2);
1326 int class_sorting1, class_sorting2;
1329 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1331 class_sorting1 = LEVELSORTING(entry1);
1332 class_sorting2 = LEVELSORTING(entry2);
1336 class_sorting1 = ARTWORKSORTING(entry1);
1337 class_sorting2 = ARTWORKSORTING(entry2);
1340 if (entry1->parent_link || entry2->parent_link)
1341 compare_result = (entry1->parent_link ? -1 : +1);
1342 else if (entry1->sort_priority == entry2->sort_priority)
1344 char *name1 = getStringToLower(entry1->name_sorting);
1345 char *name2 = getStringToLower(entry2->name_sorting);
1347 compare_result = strcmp(name1, name2);
1352 else if (class_sorting1 == class_sorting2)
1353 compare_result = entry1->sort_priority - entry2->sort_priority;
1355 compare_result = class_sorting1 - class_sorting2;
1357 return compare_result;
1360 static void createParentTreeInfoNode(TreeInfo *node_parent)
1364 if (node_parent == NULL)
1367 ti_new = newTreeInfo();
1368 setTreeInfoToDefaults(ti_new, node_parent->type);
1370 ti_new->node_parent = node_parent;
1371 ti_new->parent_link = TRUE;
1373 ti_new->identifier = getStringCopy(node_parent->identifier);
1374 ti_new->name = ".. (parent directory)";
1375 ti_new->name_sorting = getStringCopy(ti_new->name);
1377 ti_new->filename = "..";
1378 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1380 ti_new->sort_priority = node_parent->sort_priority;
1381 ti_new->class_desc = getLevelClassDescription(ti_new);
1383 pushTreeInfo(&node_parent->node_group, ti_new);
1386 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1387 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1389 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1390 TreeInfo *node_parent,
1391 char *level_directory,
1392 char *directory_name)
1394 char *directory_path = getPath2(level_directory, directory_name);
1395 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1396 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1397 LevelDirTree *leveldir_new = NULL;
1400 if (setup_file_list == NULL)
1402 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1404 free(directory_path);
1410 leveldir_new = newTreeInfo();
1413 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1415 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1417 leveldir_new->filename = getStringCopy(directory_name);
1419 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1421 /* set all structure fields according to the token/value pairs */
1422 ldi = *leveldir_new;
1423 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1424 setSetupInfo(levelinfo_tokens, i,
1425 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1426 *leveldir_new = ldi;
1428 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1430 free(leveldir_new->name);
1431 leveldir_new->name = getStringCopy(leveldir_new->filename);
1434 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1436 if (leveldir_new->identifier == NULL)
1437 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1439 if (leveldir_new->name_sorting == NULL)
1440 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1442 if (node_parent == NULL) /* top level group */
1444 leveldir_new->basepath = level_directory;
1445 leveldir_new->fullpath = leveldir_new->filename;
1447 else /* sub level group */
1449 leveldir_new->basepath = node_parent->basepath;
1450 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1453 if (leveldir_new->levels < 1)
1454 leveldir_new->levels = 1;
1456 leveldir_new->last_level =
1457 leveldir_new->first_level + leveldir_new->levels - 1;
1459 leveldir_new->user_defined =
1460 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1462 leveldir_new->color = LEVELCOLOR(leveldir_new);
1463 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1465 leveldir_new->handicap_level = /* set handicap to default value */
1466 (leveldir_new->user_defined ?
1467 leveldir_new->last_level :
1468 leveldir_new->first_level);
1470 pushTreeInfo(node_first, leveldir_new);
1472 freeSetupFileList(setup_file_list);
1474 if (leveldir_new->level_group)
1476 /* create node to link back to current level directory */
1477 createParentTreeInfoNode(leveldir_new);
1479 /* step into sub-directory and look for more level series */
1480 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1481 leveldir_new, directory_path);
1484 free(directory_path);
1490 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1491 TreeInfo *node_parent,
1492 char *level_directory)
1495 struct dirent *dir_entry;
1496 boolean valid_entry_found = FALSE;
1498 if ((dir = opendir(level_directory)) == NULL)
1500 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1504 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1506 struct stat file_status;
1507 char *directory_name = dir_entry->d_name;
1508 char *directory_path = getPath2(level_directory, directory_name);
1510 /* skip entries for current and parent directory */
1511 if (strcmp(directory_name, ".") == 0 ||
1512 strcmp(directory_name, "..") == 0)
1514 free(directory_path);
1518 /* find out if directory entry is itself a directory */
1519 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1520 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1522 free(directory_path);
1526 free(directory_path);
1528 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1529 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1530 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1533 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1540 if (!valid_entry_found)
1542 /* check if this directory directly contains a file "levelinfo.conf" */
1543 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1544 level_directory, ".");
1547 if (!valid_entry_found)
1548 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1552 void LoadLevelInfo()
1554 InitUserLevelDirectory(getLoginName());
1556 DrawInitText("Loading level series:", 120, FC_GREEN);
1558 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1559 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1561 /* before sorting, the first entries will be from the user directory */
1562 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1564 if (leveldir_first == NULL)
1565 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1567 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1570 dumpTreeInfo(leveldir_first, 0);
1574 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1575 TreeInfo *node_parent,
1576 char *base_directory,
1577 char *directory_name, int type)
1579 char *directory_path = getPath2(base_directory, directory_name);
1580 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1581 struct SetupFileList *setup_file_list = NULL;
1582 TreeInfo *artwork_new = NULL;
1585 if (access(filename, F_OK) == 0) /* file exists */
1586 setup_file_list = loadSetupFileList(filename);
1588 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1591 struct dirent *dir_entry;
1592 boolean valid_file_found = FALSE;
1594 if ((dir = opendir(directory_path)) != NULL)
1596 while ((dir_entry = readdir(dir)) != NULL)
1598 char *entry_name = dir_entry->d_name;
1600 if (FileIsArtworkType(entry_name, type))
1602 valid_file_found = TRUE;
1610 if (!valid_file_found)
1612 if (strcmp(directory_name, ".") != 0)
1613 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1615 free(directory_path);
1622 artwork_new = newTreeInfo();
1625 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1627 setTreeInfoToDefaults(artwork_new, type);
1629 artwork_new->filename = getStringCopy(directory_name);
1631 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1634 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1637 /* set all structure fields according to the token/value pairs */
1639 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1640 setSetupInfo(levelinfo_tokens, i,
1641 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1644 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1646 free(artwork_new->name);
1647 artwork_new->name = getStringCopy(artwork_new->filename);
1651 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1654 if (artwork_new->identifier == NULL)
1655 artwork_new->identifier = getStringCopy(artwork_new->filename);
1657 if (artwork_new->name_sorting == NULL)
1658 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1661 if (node_parent == NULL) /* top level group */
1663 artwork_new->basepath = getStringCopy(base_directory);
1664 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1666 else /* sub level group */
1668 artwork_new->basepath = getStringCopy(node_parent->basepath);
1669 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1672 artwork_new->user_defined =
1673 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1675 /* (may use ".sort_priority" from "setup_file_list" above) */
1676 artwork_new->color = ARTWORKCOLOR(artwork_new);
1677 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1679 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1681 if (artwork_new->name != NULL)
1682 free(artwork_new->name);
1684 if (strcmp(artwork_new->filename, ".") == 0)
1686 if (artwork_new->user_defined)
1688 artwork_new->identifier = getStringCopy("private");
1689 artwork_new->sort_priority = ARTWORKCLASS_USER;
1693 artwork_new->identifier = getStringCopy("classic");
1694 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1697 /* set to new values after changing ".sort_priority" */
1698 artwork_new->color = ARTWORKCOLOR(artwork_new);
1699 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1703 artwork_new->identifier = getStringCopy(artwork_new->filename);
1706 artwork_new->name = getStringCopy(artwork_new->identifier);
1707 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1710 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1712 pushTreeInfo(node_first, artwork_new);
1714 freeSetupFileList(setup_file_list);
1716 free(directory_path);
1722 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1723 TreeInfo *node_parent,
1724 char *base_directory, int type)
1727 struct dirent *dir_entry;
1728 boolean valid_entry_found = FALSE;
1730 if ((dir = opendir(base_directory)) == NULL)
1732 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1733 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1737 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1739 struct stat file_status;
1740 char *directory_name = dir_entry->d_name;
1741 char *directory_path = getPath2(base_directory, directory_name);
1743 /* skip entries for current and parent directory */
1744 if (strcmp(directory_name, ".") == 0 ||
1745 strcmp(directory_name, "..") == 0)
1747 free(directory_path);
1751 /* find out if directory entry is itself a directory */
1752 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1753 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1755 free(directory_path);
1759 free(directory_path);
1761 /* check if this directory contains artwork with or without config file */
1762 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1764 directory_name, type);
1769 /* check if this directory directly contains artwork itself */
1770 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1771 base_directory, ".",
1773 if (!valid_entry_found)
1774 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1778 static TreeInfo *getDummyArtworkInfo(int type)
1780 /* this is only needed when there is completely no artwork available */
1781 TreeInfo *artwork_new = newTreeInfo();
1783 setTreeInfoToDefaults(artwork_new, type);
1785 artwork_new->filename = getStringCopy(NOT_AVAILABLE);
1786 artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
1787 artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
1789 if (artwork_new->name != NULL)
1790 free(artwork_new->name);
1792 artwork_new->identifier = getStringCopy(NOT_AVAILABLE);
1793 artwork_new->name = getStringCopy(NOT_AVAILABLE);
1794 artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
1799 void LoadArtworkInfo()
1801 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1803 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1804 options.graphics_directory,
1805 TREE_TYPE_GRAPHICS_DIR);
1806 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1807 getUserGraphicsDir(),
1808 TREE_TYPE_GRAPHICS_DIR);
1810 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1811 options.sounds_directory,
1812 TREE_TYPE_SOUNDS_DIR);
1813 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1815 TREE_TYPE_SOUNDS_DIR);
1817 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1818 options.music_directory,
1819 TREE_TYPE_MUSIC_DIR);
1820 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1822 TREE_TYPE_MUSIC_DIR);
1824 if (artwork.gfx_first == NULL)
1825 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1826 if (artwork.snd_first == NULL)
1827 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1828 if (artwork.mus_first == NULL)
1829 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1831 /* before sorting, the first entries will be from the user directory */
1832 artwork.gfx_current =
1833 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1834 if (artwork.gfx_current == NULL)
1835 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1837 artwork.snd_current =
1838 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1839 if (artwork.snd_current == NULL)
1840 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1842 artwork.mus_current =
1843 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1844 if (artwork.mus_current == NULL)
1845 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1847 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1848 artwork.snd_current_identifier = artwork.snd_current->identifier;
1849 artwork.mus_current_identifier = artwork.mus_current->identifier;
1852 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1853 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1854 printf("music set == %s\n\n", artwork.mus_current_identifier);
1857 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1858 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1859 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1862 dumpTreeInfo(artwork.gfx_first, 0);
1863 dumpTreeInfo(artwork.snd_first, 0);
1864 dumpTreeInfo(artwork.mus_first, 0);
1868 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1869 LevelDirTree *level_node)
1871 /* recursively check all level directories for artwork sub-directories */
1875 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1876 ARTWORK_DIRECTORY((*artwork_node)->type));
1879 if (!level_node->parent_link)
1880 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1881 level_node->filename, level_node->name);
1884 if (!level_node->parent_link)
1886 TreeInfo *topnode_last = *artwork_node;
1888 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1889 (*artwork_node)->type);
1891 if (topnode_last != *artwork_node)
1893 free((*artwork_node)->identifier);
1894 free((*artwork_node)->name);
1895 free((*artwork_node)->name_sorting);
1897 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1898 (*artwork_node)->name = getStringCopy(level_node->name);
1899 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1901 (*artwork_node)->sort_priority = level_node->sort_priority;
1902 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1908 if (level_node->node_group != NULL)
1909 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1911 level_node = level_node->next;
1915 void LoadLevelArtworkInfo()
1917 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1919 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1920 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1921 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1923 /* needed for reloading level artwork not known at ealier stage */
1924 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
1926 artwork.gfx_current =
1927 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1928 if (artwork.gfx_current == NULL)
1929 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1932 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
1934 artwork.snd_current =
1935 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1936 if (artwork.snd_current == NULL)
1937 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1940 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
1942 artwork.mus_current =
1943 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1944 if (artwork.mus_current == NULL)
1945 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1948 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1949 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1950 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1953 dumpTreeInfo(artwork.gfx_first, 0);
1954 dumpTreeInfo(artwork.snd_first, 0);
1955 dumpTreeInfo(artwork.mus_first, 0);
1959 static void SaveUserLevelInfo()
1965 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1967 if (!(file = fopen(filename, MODE_WRITE)))
1969 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1974 /* always start with reliable default values */
1975 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1977 ldi.name = getStringCopy(getLoginName());
1978 ldi.author = getStringCopy(getRealName());
1980 ldi.first_level = 1;
1981 ldi.sort_priority = LEVELCLASS_USER_START;
1982 ldi.readonly = FALSE;
1984 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1985 getCookie("LEVELINFO")));
1987 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1988 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
1989 i != LEVELINFO_TOKEN_NAME_SORTING &&
1990 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1991 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1996 SetFilePermissions(filename, PERMS_PRIVATE);
1999 char *getSetupValue(int type, void *value)
2001 static char value_string[MAX_LINE_LEN];
2009 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2013 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2017 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2021 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2025 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2029 sprintf(value_string, "%d", *(int *)value);
2033 strcpy(value_string, *(char **)value);
2037 value_string[0] = '\0';
2041 return value_string;
2044 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2048 static char token_string[MAX_LINE_LEN];
2049 int token_type = token_info[token_nr].type;
2050 void *setup_value = token_info[token_nr].value;
2051 char *token_text = token_info[token_nr].text;
2052 char *value_string = getSetupValue(token_type, setup_value);
2054 /* build complete token string */
2055 sprintf(token_string, "%s%s", prefix, token_text);
2057 /* build setup entry line */
2058 line = getFormattedSetupEntry(token_string, value_string);
2060 if (token_type == TYPE_KEY_X11)
2062 Key key = *(Key *)setup_value;
2063 char *keyname = getKeyNameFromKey(key);
2065 /* add comment, if useful */
2066 if (strcmp(keyname, "(undefined)") != 0 &&
2067 strcmp(keyname, "(unknown)") != 0)
2069 /* add at least one whitespace */
2071 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2075 strcat(line, keyname);
2082 void LoadLevelSetup_LastSeries()
2085 struct SetupFileList *level_setup_list = NULL;
2087 /* always start with reliable default values */
2088 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2090 /* ----------------------------------------------------------------------- */
2091 /* ~/.<program>/levelsetup.conf */
2092 /* ----------------------------------------------------------------------- */
2094 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2096 if ((level_setup_list = loadSetupFileList(filename)))
2098 char *last_level_series =
2099 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2101 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2103 if (leveldir_current == NULL)
2104 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2106 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2108 freeSetupFileList(level_setup_list);
2111 Error(ERR_WARN, "using default setup values");
2116 void SaveLevelSetup_LastSeries()
2119 char *level_subdir = leveldir_current->filename;
2122 /* ----------------------------------------------------------------------- */
2123 /* ~/.<program>/levelsetup.conf */
2124 /* ----------------------------------------------------------------------- */
2126 InitUserDataDirectory();
2128 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2130 if (!(file = fopen(filename, MODE_WRITE)))
2132 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2137 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2138 getCookie("LEVELSETUP")));
2139 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2145 SetFilePermissions(filename, PERMS_PRIVATE);
2148 static void checkSeriesInfo()
2150 static char *level_directory = NULL;
2152 struct dirent *dir_entry;
2154 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2156 level_directory = getPath2((leveldir_current->user_defined ?
2157 getUserLevelDir(NULL) :
2158 options.level_directory),
2159 leveldir_current->fullpath);
2161 if ((dir = opendir(level_directory)) == NULL)
2163 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2167 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2169 if (strlen(dir_entry->d_name) > 4 &&
2170 dir_entry->d_name[3] == '.' &&
2171 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2173 char levelnum_str[4];
2176 strncpy(levelnum_str, dir_entry->d_name, 3);
2177 levelnum_str[3] = '\0';
2179 levelnum_value = atoi(levelnum_str);
2181 if (levelnum_value < leveldir_current->first_level)
2183 Error(ERR_WARN, "additional level %d found", levelnum_value);
2184 leveldir_current->first_level = levelnum_value;
2186 else if (levelnum_value > leveldir_current->last_level)
2188 Error(ERR_WARN, "additional level %d found", levelnum_value);
2189 leveldir_current->last_level = levelnum_value;
2197 void LoadLevelSetup_SeriesInfo()
2200 struct SetupFileList *level_setup_list = NULL;
2201 char *level_subdir = leveldir_current->filename;
2203 /* always start with reliable default values */
2204 level_nr = leveldir_current->first_level;
2206 checkSeriesInfo(leveldir_current);
2208 /* ----------------------------------------------------------------------- */
2209 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2210 /* ----------------------------------------------------------------------- */
2212 level_subdir = leveldir_current->filename;
2214 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2216 if ((level_setup_list = loadSetupFileList(filename)))
2220 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2224 level_nr = atoi(token_value);
2226 if (level_nr < leveldir_current->first_level)
2227 level_nr = leveldir_current->first_level;
2228 if (level_nr > leveldir_current->last_level)
2229 level_nr = leveldir_current->last_level;
2232 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2236 int level_nr = atoi(token_value);
2238 if (level_nr < leveldir_current->first_level)
2239 level_nr = leveldir_current->first_level;
2240 if (level_nr > leveldir_current->last_level + 1)
2241 level_nr = leveldir_current->last_level;
2243 if (leveldir_current->user_defined)
2244 level_nr = leveldir_current->last_level;
2246 leveldir_current->handicap_level = level_nr;
2249 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2251 freeSetupFileList(level_setup_list);
2254 Error(ERR_WARN, "using default setup values");
2259 void SaveLevelSetup_SeriesInfo()
2262 char *level_subdir = leveldir_current->filename;
2263 char *level_nr_str = int2str(level_nr, 0);
2264 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2267 /* ----------------------------------------------------------------------- */
2268 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2269 /* ----------------------------------------------------------------------- */
2271 InitLevelSetupDirectory(level_subdir);
2273 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2275 if (!(file = fopen(filename, MODE_WRITE)))
2277 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2282 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2283 getCookie("LEVELSETUP")));
2284 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2286 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2287 handicap_level_str));
2292 SetFilePermissions(filename, PERMS_PRIVATE);