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 TOKEN_VALUE_POSITION 40
84 #define TOKEN_COMMENT_POSITION 60
86 #define MAX_COOKIE_LEN 256
88 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
89 GRAPHICSINFO_FILENAME : \
90 (type) == TREE_TYPE_SOUNDS_DIR ? \
91 SOUNDSINFO_FILENAME : \
92 (type) == TREE_TYPE_MUSIC_DIR ? \
93 MUSICINFO_FILENAME : "")
95 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
96 GRAPHICS_DIRECTORY : \
97 (type) == TREE_TYPE_SOUNDS_DIR ? \
99 (type) == TREE_TYPE_MUSIC_DIR ? \
100 MUSIC_DIRECTORY : "")
102 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
103 options.graphics_directory : \
104 (type) == TREE_TYPE_SOUNDS_DIR ? \
105 options.sounds_directory : \
106 (type) == TREE_TYPE_MUSIC_DIR ? \
107 options.music_directory : "")
110 /* ------------------------------------------------------------------------- */
112 /* ------------------------------------------------------------------------- */
114 static char *getLevelClassDescription(TreeInfo *ldi)
116 int position = ldi->sort_priority / 100;
118 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
119 return levelclass_desc[position];
121 return "Unknown Level Class";
124 static char *getUserLevelDir(char *level_subdir)
126 static char *userlevel_dir = NULL;
127 char *data_dir = getUserDataDir();
128 char *userlevel_subdir = LEVELS_DIRECTORY;
133 if (level_subdir != NULL)
134 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
136 userlevel_dir = getPath2(data_dir, userlevel_subdir);
138 return userlevel_dir;
141 static char *getTapeDir(char *level_subdir)
143 static char *tape_dir = NULL;
144 char *data_dir = getUserDataDir();
145 char *tape_subdir = TAPES_DIRECTORY;
150 if (level_subdir != NULL)
151 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
153 tape_dir = getPath2(data_dir, tape_subdir);
158 static char *getScoreDir(char *level_subdir)
160 static char *score_dir = NULL;
161 char *data_dir = options.rw_base_directory;
162 char *score_subdir = SCORES_DIRECTORY;
167 if (level_subdir != NULL)
168 score_dir = getPath3(data_dir, score_subdir, level_subdir);
170 score_dir = getPath2(data_dir, score_subdir);
175 static char *getLevelSetupDir(char *level_subdir)
177 static char *levelsetup_dir = NULL;
178 char *data_dir = getUserDataDir();
179 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
182 free(levelsetup_dir);
184 if (level_subdir != NULL)
185 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
187 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
189 return levelsetup_dir;
192 static char *getLevelDirFromTreeInfo(TreeInfo *node)
194 static char *level_dir = NULL;
197 return options.level_directory;
202 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
203 options.level_directory), node->fullpath);
208 static char *getCurrentLevelDir()
210 return getLevelDirFromTreeInfo(leveldir_current);
213 static char *getDefaultGraphicsDir(char *graphics_subdir)
215 static char *graphics_dir = NULL;
217 if (graphics_subdir == NULL)
218 return options.graphics_directory;
223 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
228 static char *getDefaultSoundsDir(char *sounds_subdir)
230 static char *sounds_dir = NULL;
232 if (sounds_subdir == NULL)
233 return options.sounds_directory;
238 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
243 static char *getDefaultMusicDir(char *music_subdir)
245 static char *music_dir = NULL;
247 if (music_subdir == NULL)
248 return options.music_directory;
253 music_dir = getPath2(options.music_directory, music_subdir);
258 static char *getUserGraphicsDir()
260 static char *usergraphics_dir = NULL;
262 if (usergraphics_dir == NULL)
263 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
265 return usergraphics_dir;
268 static char *getUserSoundsDir()
270 static char *usersounds_dir = NULL;
272 if (usersounds_dir == NULL)
273 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
275 return usersounds_dir;
278 static char *getUserMusicDir()
280 static char *usermusic_dir = NULL;
282 if (usermusic_dir == NULL)
283 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
285 return usermusic_dir;
288 char *getLevelFilename(int nr)
290 static char *filename = NULL;
291 char basename[MAX_FILENAME_LEN];
293 if (filename != NULL)
296 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
297 filename = getPath2(getCurrentLevelDir(), basename);
302 char *getTapeFilename(int nr)
304 static char *filename = NULL;
305 char basename[MAX_FILENAME_LEN];
307 if (filename != NULL)
310 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
311 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
316 char *getScoreFilename(int nr)
318 static char *filename = NULL;
319 char basename[MAX_FILENAME_LEN];
321 if (filename != NULL)
324 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
325 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
330 char *getSetupFilename()
332 static char *filename = NULL;
334 if (filename != NULL)
337 filename = getPath2(getSetupDir(), SETUP_FILENAME);
342 static char *getSetupArtworkDir(TreeInfo *ti)
344 static char *artwork_dir = NULL;
346 if (artwork_dir != NULL)
349 artwork_dir = getPath2(ti->basepath, ti->fullpath);
354 static char *getCorrectedImageBasename(char *basename)
356 char *result = basename;
358 #if defined(PLATFORM_MSDOS)
359 if (program.filename_prefix != NULL)
361 int prefix_len = strlen(program.filename_prefix);
363 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
364 result = &basename[prefix_len];
371 static boolean fileExists(char *filename)
374 printf("checking file '%s'\n", filename);
377 return (access(filename, F_OK) == 0);
380 char *getCustomImageFilename(char *basename)
382 static char *filename = NULL;
384 if (filename != NULL)
387 basename = getCorrectedImageBasename(basename);
389 if (!setup.override_level_graphics)
391 /* 1st try: look for special artwork in current level series directory */
392 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
393 if (fileExists(filename))
397 /* 2nd try: look for special artwork in configured artwork directory */
398 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
399 if (fileExists(filename))
402 /* 3rd try: look for default artwork in new default artwork directory */
403 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
404 if (fileExists(filename))
407 /* 4th try: look for default artwork in old default artwork directory */
408 filename = getPath2(options.graphics_directory, basename);
409 if (fileExists(filename))
412 return NULL; /* cannot find specified artwork file anywhere */
415 char *getCustomSoundFilename(char *basename)
417 static char *filename = NULL;
419 if (filename != NULL)
422 if (!setup.override_level_sounds)
424 /* 1st try: look for special artwork in current level series directory */
425 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
426 if (fileExists(filename))
430 /* 2nd try: look for special artwork in configured artwork directory */
431 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
432 if (fileExists(filename))
435 /* 3rd try: look for default artwork in new default artwork directory */
436 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
437 if (fileExists(filename))
440 /* 4th try: look for default artwork in old default artwork directory */
441 filename = getPath2(options.sounds_directory, basename);
442 if (fileExists(filename))
445 return NULL; /* cannot find specified artwork file anywhere */
448 char *getCustomSoundConfigFilename()
450 return getCustomSoundFilename(SOUNDSINFO_FILENAME);
453 char *getCustomMusicDirectory(void)
455 static char *directory = NULL;
457 if (directory != NULL)
460 if (!setup.override_level_music)
462 /* 1st try: look for special artwork in current level series directory */
463 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
464 if (fileExists(directory))
468 /* 2nd try: look for special artwork in configured artwork directory */
469 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
470 if (fileExists(directory))
473 /* 3rd try: look for default artwork in new default artwork directory */
474 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
475 if (fileExists(directory))
478 /* 4th try: look for default artwork in old default artwork directory */
479 directory = getStringCopy(options.music_directory);
480 if (fileExists(directory))
483 return NULL; /* cannot find specified artwork file anywhere */
486 void InitTapeDirectory(char *level_subdir)
488 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
489 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
490 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
493 void InitScoreDirectory(char *level_subdir)
495 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
496 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
499 static void SaveUserLevelInfo();
501 void InitUserLevelDirectory(char *level_subdir)
503 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
505 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
506 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
507 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
513 void InitLevelSetupDirectory(char *level_subdir)
515 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
516 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
517 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
521 /* ------------------------------------------------------------------------- */
522 /* some functions to handle lists of level directories */
523 /* ------------------------------------------------------------------------- */
525 TreeInfo *newTreeInfo()
527 return checked_calloc(sizeof(TreeInfo));
530 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
532 node_new->next = *node_first;
533 *node_first = node_new;
536 int numTreeInfo(TreeInfo *node)
549 boolean validLevelSeries(TreeInfo *node)
551 return (node != NULL && !node->node_group && !node->parent_link);
554 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
559 if (node->node_group) /* enter level group (step down into tree) */
560 return getFirstValidTreeInfoEntry(node->node_group);
561 else if (node->parent_link) /* skip start entry of level group */
563 if (node->next) /* get first real level series entry */
564 return getFirstValidTreeInfoEntry(node->next);
565 else /* leave empty level group and go on */
566 return getFirstValidTreeInfoEntry(node->node_parent->next);
568 else /* this seems to be a regular level series */
572 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
577 if (node->node_parent == NULL) /* top level group */
578 return *node->node_top;
579 else /* sub level group */
580 return node->node_parent->node_group;
583 int numTreeInfoInGroup(TreeInfo *node)
585 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
588 int posTreeInfo(TreeInfo *node)
590 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
595 if (node_cmp == node)
599 node_cmp = node_cmp->next;
605 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
607 TreeInfo *node_default = node;
622 TreeInfo *getTreeInfoFromFilenameExt(TreeInfo *node, char *filename)
624 if (filename == NULL)
629 if (node->node_group)
631 TreeInfo *node_group;
633 node_group = getTreeInfoFromFilenameExt(node->node_group, filename);
638 else if (!node->parent_link)
640 if (strcmp(filename, node->filename) == 0)
643 /* special case when looking for level series artwork:
644 node->name_short contains level series filename */
645 if (strcmp(node->filename, ".") == 0 &&
646 strcmp(filename, node->name_short) == 0)
656 TreeInfo *getTreeInfoFromFilename(TreeInfo *ti, char *filename)
658 return getTreeInfoFromFilenameExt(ti, filename);
661 void dumpTreeInfo(TreeInfo *node, int depth)
665 printf("Dumping TreeInfo:\n");
669 for (i=0; i<(depth + 1) * 3; i++)
672 printf("filename == '%s' (%s) [%s]\n",
673 node->filename, node->name, node->name_short);
675 if (node->node_group != NULL)
676 dumpTreeInfo(node->node_group, depth + 1);
682 void sortTreeInfo(TreeInfo **node_first,
683 int (*compare_function)(const void *, const void *))
685 int num_nodes = numTreeInfo(*node_first);
686 TreeInfo **sort_array;
687 TreeInfo *node = *node_first;
693 /* allocate array for sorting structure pointers */
694 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
696 /* writing structure pointers to sorting array */
697 while (i < num_nodes && node) /* double boundary check... */
699 sort_array[i] = node;
705 /* sorting the structure pointers in the sorting array */
706 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
709 /* update the linkage of list elements with the sorted node array */
710 for (i=0; i<num_nodes - 1; i++)
711 sort_array[i]->next = sort_array[i + 1];
712 sort_array[num_nodes - 1]->next = NULL;
714 /* update the linkage of the main list anchor pointer */
715 *node_first = sort_array[0];
719 /* now recursively sort the level group structures */
723 if (node->node_group != NULL)
724 sortTreeInfo(&node->node_group, compare_function);
731 /* ========================================================================= */
732 /* some stuff from "files.c" */
733 /* ========================================================================= */
735 #if defined(PLATFORM_WIN32)
737 #define S_IRGRP S_IRUSR
740 #define S_IROTH S_IRUSR
743 #define S_IWGRP S_IWUSR
746 #define S_IWOTH S_IWUSR
749 #define S_IXGRP S_IXUSR
752 #define S_IXOTH S_IXUSR
755 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
760 #endif /* PLATFORM_WIN32 */
762 /* file permissions for newly written files */
763 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
764 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
765 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
767 #define MODE_W_PRIVATE (S_IWUSR)
768 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
769 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
771 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
772 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
774 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
775 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
777 char *getUserDataDir(void)
779 static char *userdata_dir = NULL;
783 char *home_dir = getHomeDir();
784 char *data_dir = program.userdata_directory;
786 userdata_dir = getPath2(home_dir, data_dir);
794 return getUserDataDir();
797 static mode_t posix_umask(mode_t mask)
799 #if defined(PLATFORM_UNIX)
806 static int posix_mkdir(const char *pathname, mode_t mode)
808 #if defined(PLATFORM_WIN32)
809 return mkdir(pathname);
811 return mkdir(pathname, mode);
815 void createDirectory(char *dir, char *text, int permission_class)
817 /* leave "other" permissions in umask untouched, but ensure group parts
818 of USERDATA_DIR_MODE are not masked */
819 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
820 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
821 mode_t normal_umask = posix_umask(0);
822 mode_t group_umask = ~(dir_mode & S_IRWXG);
823 posix_umask(normal_umask & group_umask);
825 if (access(dir, F_OK) != 0)
826 if (posix_mkdir(dir, dir_mode) != 0)
827 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
829 posix_umask(normal_umask); /* reset normal umask */
832 void InitUserDataDirectory()
834 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
837 void SetFilePermissions(char *filename, int permission_class)
839 chmod(filename, (permission_class == PERMS_PRIVATE ?
840 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
843 char *getCookie(char *file_type)
845 static char cookie[MAX_COOKIE_LEN + 1];
847 if (strlen(program.cookie_prefix) + 1 +
848 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
849 return "[COOKIE ERROR]"; /* should never happen */
851 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
852 program.cookie_prefix, file_type,
853 program.version_major, program.version_minor);
858 int getFileVersionFromCookieString(const char *cookie)
860 const char *ptr_cookie1, *ptr_cookie2;
861 const char *pattern1 = "_FILE_VERSION_";
862 const char *pattern2 = "?.?";
863 const int len_cookie = strlen(cookie);
864 const int len_pattern1 = strlen(pattern1);
865 const int len_pattern2 = strlen(pattern2);
866 const int len_pattern = len_pattern1 + len_pattern2;
867 int version_major, version_minor;
869 if (len_cookie <= len_pattern)
872 ptr_cookie1 = &cookie[len_cookie - len_pattern];
873 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
875 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
878 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
879 ptr_cookie2[1] != '.' ||
880 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
883 version_major = ptr_cookie2[0] - '0';
884 version_minor = ptr_cookie2[2] - '0';
886 return VERSION_IDENT(version_major, version_minor, 0);
889 boolean checkCookieString(const char *cookie, const char *template)
891 const char *pattern = "_FILE_VERSION_?.?";
892 const int len_cookie = strlen(cookie);
893 const int len_template = strlen(template);
894 const int len_pattern = strlen(pattern);
896 if (len_cookie != len_template)
899 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
905 /* ------------------------------------------------------------------------- */
906 /* setup file list handling functions */
907 /* ------------------------------------------------------------------------- */
909 int get_string_integer_value(char *s)
911 static char *number_text[][3] =
913 { "0", "zero", "null", },
914 { "1", "one", "first" },
915 { "2", "two", "second" },
916 { "3", "three", "third" },
917 { "4", "four", "fourth" },
918 { "5", "five", "fifth" },
919 { "6", "six", "sixth" },
920 { "7", "seven", "seventh" },
921 { "8", "eight", "eighth" },
922 { "9", "nine", "ninth" },
923 { "10", "ten", "tenth" },
924 { "11", "eleven", "eleventh" },
925 { "12", "twelve", "twelfth" },
929 char *s_lower = getStringToLower(s);
934 if (strcmp(s_lower, number_text[i][j]) == 0)
945 boolean get_string_boolean_value(char *s)
947 char *s_lower = getStringToLower(s);
948 boolean result = FALSE;
950 if (strcmp(s_lower, "true") == 0 ||
951 strcmp(s_lower, "yes") == 0 ||
952 strcmp(s_lower, "on") == 0 ||
953 get_string_integer_value(s) == 1)
961 char *getFormattedSetupEntry(char *token, char *value)
964 static char entry[MAX_LINE_LEN];
966 /* start with the token and some spaces to format output line */
967 sprintf(entry, "%s:", token);
968 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
971 /* continue with the token's value */
972 strcat(entry, value);
977 void freeSetupFileList(struct SetupFileList *setup_file_list)
979 if (!setup_file_list)
982 if (setup_file_list->token)
983 free(setup_file_list->token);
984 if (setup_file_list->value)
985 free(setup_file_list->value);
986 if (setup_file_list->next)
987 freeSetupFileList(setup_file_list->next);
988 free(setup_file_list);
991 static struct SetupFileList *newSetupFileList(char *token, char *value)
993 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
995 new->token = checked_malloc(strlen(token) + 1);
996 strcpy(new->token, token);
998 new->value = checked_malloc(strlen(value) + 1);
999 strcpy(new->value, value);
1006 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1008 if (!setup_file_list)
1011 if (strcmp(setup_file_list->token, token) == 0)
1012 return setup_file_list->value;
1014 return getTokenValue(setup_file_list->next, token);
1017 static void setTokenValue(struct SetupFileList *setup_file_list,
1018 char *token, char *value)
1020 if (!setup_file_list)
1023 if (strcmp(setup_file_list->token, token) == 0)
1025 free(setup_file_list->value);
1026 setup_file_list->value = checked_malloc(strlen(value) + 1);
1027 strcpy(setup_file_list->value, value);
1029 else if (setup_file_list->next == NULL)
1030 setup_file_list->next = newSetupFileList(token, value);
1032 setTokenValue(setup_file_list->next, token, value);
1036 static void printSetupFileList(struct SetupFileList *setup_file_list)
1038 if (!setup_file_list)
1041 printf("token: '%s'\n", setup_file_list->token);
1042 printf("value: '%s'\n", setup_file_list->value);
1044 printSetupFileList(setup_file_list->next);
1048 struct SetupFileList *loadSetupFileList(char *filename)
1051 char line[MAX_LINE_LEN];
1052 char *token, *value, *line_ptr;
1053 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1054 struct SetupFileList *first_valid_list_entry;
1058 if (!(file = fopen(filename, MODE_READ)))
1060 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1066 /* read next line of input file */
1067 if (!fgets(line, MAX_LINE_LEN, file))
1070 /* cut trailing comment or whitespace from input line */
1071 for (line_ptr = line; *line_ptr; line_ptr++)
1073 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1080 /* cut trailing whitespaces from input line */
1081 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1082 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1085 /* ignore empty lines */
1089 line_len = strlen(line);
1091 /* cut leading whitespaces from token */
1092 for (token = line; *token; token++)
1093 if (*token != ' ' && *token != '\t')
1096 /* find end of token */
1097 for (line_ptr = token; *line_ptr; line_ptr++)
1099 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1106 if (line_ptr < line + line_len)
1107 value = line_ptr + 1;
1111 /* cut leading whitespaces from value */
1112 for (; *value; value++)
1113 if (*value != ' ' && *value != '\t')
1116 if (*token && *value)
1117 setTokenValue(setup_file_list, token, value);
1122 first_valid_list_entry = setup_file_list->next;
1124 /* free empty list header */
1125 setup_file_list->next = NULL;
1126 freeSetupFileList(setup_file_list);
1128 if (first_valid_list_entry == NULL)
1129 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1131 return first_valid_list_entry;
1134 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1137 if (!setup_file_list)
1140 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1142 if (!checkCookieString(setup_file_list->value, identifier))
1144 Error(ERR_WARN, "configuration file has wrong file identifier");
1151 if (setup_file_list->next)
1152 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1155 Error(ERR_WARN, "configuration file has no file identifier");
1161 /* ========================================================================= */
1162 /* setup file stuff */
1163 /* ========================================================================= */
1165 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1166 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1167 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1169 /* level directory info */
1170 #define LEVELINFO_TOKEN_NAME 0
1171 #define LEVELINFO_TOKEN_NAME_SHORT 1
1172 #define LEVELINFO_TOKEN_NAME_SORTING 2
1173 #define LEVELINFO_TOKEN_AUTHOR 3
1174 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1175 #define LEVELINFO_TOKEN_LEVELS 5
1176 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1177 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1178 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1179 #define LEVELINFO_TOKEN_READONLY 9
1181 #define NUM_LEVELINFO_TOKENS 10
1183 static LevelDirTree ldi;
1185 static struct TokenInfo levelinfo_tokens[] =
1187 /* level directory info */
1188 { TYPE_STRING, &ldi.name, "name" },
1189 { TYPE_STRING, &ldi.name_short, "name_short" },
1190 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1191 { TYPE_STRING, &ldi.author, "author" },
1192 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1193 { TYPE_INTEGER, &ldi.levels, "levels" },
1194 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1195 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1196 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1197 { TYPE_BOOLEAN, &ldi.readonly, "readonly" }
1200 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1204 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1205 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1206 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1207 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1210 ldi->node_parent = NULL;
1211 ldi->node_group = NULL;
1215 ldi->cl_cursor = -1;
1217 ldi->filename = NULL;
1218 ldi->fullpath = NULL;
1219 ldi->basepath = NULL;
1220 ldi->name = getStringCopy(ANONYMOUS_NAME);
1221 ldi->name_short = NULL;
1222 ldi->name_sorting = NULL;
1223 ldi->author = getStringCopy(ANONYMOUS_NAME);
1225 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1226 ldi->parent_link = FALSE;
1227 ldi->user_defined = FALSE;
1229 ldi->class_desc = NULL;
1231 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1233 ldi->imported_from = NULL;
1235 ldi->first_level = 0;
1236 ldi->last_level = 0;
1237 ldi->level_group = FALSE;
1238 ldi->handicap_level = 0;
1239 ldi->readonly = TRUE;
1243 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1247 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1249 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1253 /* first copy all values from the parent structure ... */
1256 /* ... then set all fields to default that cannot be inherited from parent.
1257 This is especially important for all those fields that can be set from
1258 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1259 calls 'free()' for all already set token values which requires that no
1260 other structure's pointer may point to them!
1263 ldi->filename = NULL;
1264 ldi->fullpath = NULL;
1265 ldi->basepath = NULL;
1266 ldi->name = getStringCopy(ANONYMOUS_NAME);
1267 ldi->name_short = NULL;
1268 ldi->name_sorting = NULL;
1269 ldi->author = getStringCopy(parent->author);
1270 ldi->imported_from = getStringCopy(parent->imported_from);
1272 ldi->level_group = FALSE;
1273 ldi->parent_link = FALSE;
1275 ldi->node_top = parent->node_top;
1276 ldi->node_parent = parent;
1277 ldi->node_group = NULL;
1281 void setSetupInfo(struct TokenInfo *token_info,
1282 int token_nr, char *token_value)
1284 int token_type = token_info[token_nr].type;
1285 void *setup_value = token_info[token_nr].value;
1287 if (token_value == NULL)
1290 /* set setup field to corresponding token value */
1295 *(boolean *)setup_value = get_string_boolean_value(token_value);
1299 *(Key *)setup_value = getKeyFromKeyName(token_value);
1303 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1307 *(int *)setup_value = get_string_integer_value(token_value);
1311 if (*(char **)setup_value != NULL)
1312 free(*(char **)setup_value);
1313 *(char **)setup_value = getStringCopy(token_value);
1321 static int compareTreeInfoEntries(const void *object1, const void *object2)
1323 const TreeInfo *entry1 = *((TreeInfo **)object1);
1324 const TreeInfo *entry2 = *((TreeInfo **)object2);
1327 if (entry1->parent_link || entry2->parent_link)
1328 compare_result = (entry1->parent_link ? -1 : +1);
1329 else if (entry1->sort_priority == entry2->sort_priority)
1331 char *name1 = getStringToLower(entry1->name_sorting);
1332 char *name2 = getStringToLower(entry2->name_sorting);
1334 compare_result = strcmp(name1, name2);
1339 else if (LEVELSORTING(entry1) == LEVELSORTING(entry2))
1340 compare_result = entry1->sort_priority - entry2->sort_priority;
1342 compare_result = LEVELSORTING(entry1) - LEVELSORTING(entry2);
1344 return compare_result;
1347 static void createParentTreeInfoNode(TreeInfo *node_parent)
1351 if (node_parent == NULL)
1354 ti_new = newTreeInfo();
1355 setTreeInfoToDefaults(ti_new, node_parent->type);
1357 ti_new->node_parent = node_parent;
1358 ti_new->parent_link = TRUE;
1360 ti_new->name = ".. (parent directory)";
1361 ti_new->name_short = getStringCopy(ti_new->name);
1362 ti_new->name_sorting = getStringCopy(ti_new->name);
1364 ti_new->filename = "..";
1365 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1367 ti_new->sort_priority = node_parent->sort_priority;
1368 ti_new->class_desc = getLevelClassDescription(ti_new);
1370 pushTreeInfo(&node_parent->node_group, ti_new);
1373 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1374 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1376 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1377 TreeInfo *node_parent,
1378 char *level_directory,
1379 char *directory_name)
1381 char *directory_path = getPath2(level_directory, directory_name);
1382 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1383 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1384 LevelDirTree *leveldir_new = NULL;
1387 if (setup_file_list == NULL)
1389 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1391 free(directory_path);
1397 leveldir_new = newTreeInfo();
1400 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1402 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1404 leveldir_new->filename = getStringCopy(directory_name);
1406 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1408 /* set all structure fields according to the token/value pairs */
1409 ldi = *leveldir_new;
1410 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1411 setSetupInfo(levelinfo_tokens, i,
1412 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1413 *leveldir_new = ldi;
1415 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1417 free(leveldir_new->name);
1418 leveldir_new->name = getStringCopy(leveldir_new->filename);
1421 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1423 if (leveldir_new->name_short == NULL)
1424 leveldir_new->name_short = getStringCopy(leveldir_new->name);
1426 if (leveldir_new->name_sorting == NULL)
1427 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1429 if (node_parent == NULL) /* top level group */
1431 leveldir_new->basepath = level_directory;
1432 leveldir_new->fullpath = leveldir_new->filename;
1434 else /* sub level group */
1436 leveldir_new->basepath = node_parent->basepath;
1437 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1440 if (leveldir_new->levels < 1)
1441 leveldir_new->levels = 1;
1443 leveldir_new->last_level =
1444 leveldir_new->first_level + leveldir_new->levels - 1;
1446 leveldir_new->user_defined =
1447 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1449 leveldir_new->color = LEVELCOLOR(leveldir_new);
1450 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1452 leveldir_new->handicap_level = /* set handicap to default value */
1453 (leveldir_new->user_defined ?
1454 leveldir_new->last_level :
1455 leveldir_new->first_level);
1457 pushTreeInfo(node_first, leveldir_new);
1459 freeSetupFileList(setup_file_list);
1461 if (leveldir_new->level_group)
1463 /* create node to link back to current level directory */
1464 createParentTreeInfoNode(leveldir_new);
1466 /* step into sub-directory and look for more level series */
1467 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1468 leveldir_new, directory_path);
1471 free(directory_path);
1477 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1478 TreeInfo *node_parent,
1479 char *level_directory)
1482 struct dirent *dir_entry;
1483 boolean valid_entry_found = FALSE;
1485 if ((dir = opendir(level_directory)) == NULL)
1487 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1491 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1493 struct stat file_status;
1494 char *directory_name = dir_entry->d_name;
1495 char *directory_path = getPath2(level_directory, directory_name);
1497 /* skip entries for current and parent directory */
1498 if (strcmp(directory_name, ".") == 0 ||
1499 strcmp(directory_name, "..") == 0)
1501 free(directory_path);
1505 /* find out if directory entry is itself a directory */
1506 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1507 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1509 free(directory_path);
1513 free(directory_path);
1515 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1516 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1517 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1520 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1527 if (!valid_entry_found)
1529 /* check if this directory directly contains a file "levelinfo.conf" */
1530 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1531 level_directory, ".");
1534 if (!valid_entry_found)
1535 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1539 void LoadLevelInfo()
1541 InitUserLevelDirectory(getLoginName());
1543 DrawInitText("Loading level series:", 120, FC_GREEN);
1545 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1546 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1548 /* before sorting, the first entries will be from the user directory */
1549 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1551 if (leveldir_first == NULL)
1552 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1554 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1557 dumpTreeInfo(leveldir_first, 0);
1561 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1562 TreeInfo *node_parent,
1563 char *base_directory,
1564 char *directory_name, int type)
1566 char *directory_path = getPath2(base_directory, directory_name);
1567 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1568 struct SetupFileList *setup_file_list = NULL;
1569 TreeInfo *artwork_new = NULL;
1572 if (access(filename, F_OK) == 0) /* file exists */
1573 setup_file_list = loadSetupFileList(filename);
1575 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1578 struct dirent *dir_entry;
1579 boolean valid_file_found = FALSE;
1581 if ((dir = opendir(directory_path)) != NULL)
1583 while ((dir_entry = readdir(dir)) != NULL)
1585 char *entry_name = dir_entry->d_name;
1587 if (FileIsArtworkType(entry_name, type))
1589 valid_file_found = TRUE;
1597 if (!valid_file_found)
1599 if (strcmp(directory_name, ".") != 0)
1600 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1602 free(directory_path);
1609 artwork_new = newTreeInfo();
1612 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1614 setTreeInfoToDefaults(artwork_new, type);
1616 artwork_new->filename = getStringCopy(directory_name);
1618 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1621 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1624 /* set all structure fields according to the token/value pairs */
1626 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1627 setSetupInfo(levelinfo_tokens, i,
1628 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1631 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1633 free(artwork_new->name);
1634 artwork_new->name = getStringCopy(artwork_new->filename);
1638 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1641 if (artwork_new->name_short == NULL)
1642 artwork_new->name_short = getStringCopy(artwork_new->name);
1644 if (artwork_new->name_sorting == NULL)
1645 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1648 if (node_parent == NULL) /* top level group */
1650 artwork_new->basepath = getStringCopy(base_directory);
1651 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1653 else /* sub level group */
1655 artwork_new->basepath = getStringCopy(node_parent->basepath);
1656 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1659 artwork_new->user_defined =
1660 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1662 /* (may use ".sort_priority" from "setup_file_list" above) */
1663 artwork_new->color = LEVELCOLOR(artwork_new);
1664 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1666 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1668 if (artwork_new->name != NULL)
1669 free(artwork_new->name);
1671 if (strcmp(artwork_new->filename, ".") == 0)
1673 if (artwork_new->user_defined)
1675 artwork_new->name = getStringCopy("private");
1676 artwork_new->sort_priority = LEVELCLASS_USER;
1680 artwork_new->name = getStringCopy("classic");
1681 artwork_new->sort_priority = LEVELCLASS_CLASSICS;
1684 artwork_new->color = LEVELCOLOR(artwork_new);
1685 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1688 artwork_new->name = getStringCopy(artwork_new->filename);
1690 artwork_new->name_short = getStringCopy(artwork_new->name);
1691 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1694 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1696 pushTreeInfo(node_first, artwork_new);
1698 freeSetupFileList(setup_file_list);
1700 free(directory_path);
1706 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1707 TreeInfo *node_parent,
1708 char *base_directory, int type)
1711 struct dirent *dir_entry;
1712 boolean valid_entry_found = FALSE;
1714 if ((dir = opendir(base_directory)) == NULL)
1716 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1717 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1721 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1723 struct stat file_status;
1724 char *directory_name = dir_entry->d_name;
1725 char *directory_path = getPath2(base_directory, directory_name);
1727 /* skip entries for current and parent directory */
1728 if (strcmp(directory_name, ".") == 0 ||
1729 strcmp(directory_name, "..") == 0)
1731 free(directory_path);
1735 /* find out if directory entry is itself a directory */
1736 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1737 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1739 free(directory_path);
1743 free(directory_path);
1745 /* check if this directory contains artwork with or without config file */
1746 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1748 directory_name, type);
1753 /* check if this directory directly contains artwork itself */
1754 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1755 base_directory, ".",
1757 if (!valid_entry_found)
1758 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1762 static TreeInfo *getDummyArtworkInfo(int type)
1764 /* this is only needed when there is completely no artwork available */
1765 TreeInfo *artwork_new = newTreeInfo();
1767 setTreeInfoToDefaults(artwork_new, type);
1769 artwork_new->filename = getStringCopy(NOT_AVAILABLE);
1770 artwork_new->fullpath = getStringCopy(NOT_AVAILABLE);
1771 artwork_new->basepath = getStringCopy(NOT_AVAILABLE);
1773 if (artwork_new->name != NULL)
1774 free(artwork_new->name);
1776 artwork_new->name = getStringCopy(NOT_AVAILABLE);
1777 artwork_new->name_short = getStringCopy(NOT_AVAILABLE);
1778 artwork_new->name_sorting = getStringCopy(NOT_AVAILABLE);
1783 void LoadArtworkInfo()
1785 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1787 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1788 options.graphics_directory,
1789 TREE_TYPE_GRAPHICS_DIR);
1790 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1791 getUserGraphicsDir(),
1792 TREE_TYPE_GRAPHICS_DIR);
1794 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1795 options.sounds_directory,
1796 TREE_TYPE_SOUNDS_DIR);
1797 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1799 TREE_TYPE_SOUNDS_DIR);
1801 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1802 options.music_directory,
1803 TREE_TYPE_MUSIC_DIR);
1804 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1806 TREE_TYPE_MUSIC_DIR);
1808 if (artwork.gfx_first == NULL)
1809 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1810 if (artwork.snd_first == NULL)
1811 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1812 if (artwork.mus_first == NULL)
1813 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1815 /* before sorting, the first entries will be from the user directory */
1816 artwork.gfx_current =
1817 getTreeInfoFromFilename(artwork.gfx_first, setup.graphics_set);
1818 if (artwork.gfx_current == NULL)
1819 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1821 artwork.snd_current =
1822 getTreeInfoFromFilename(artwork.snd_first, setup.sounds_set);
1823 if (artwork.snd_current == NULL)
1824 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1826 artwork.mus_current =
1827 getTreeInfoFromFilename(artwork.mus_first, setup.music_set);
1828 if (artwork.mus_current == NULL)
1829 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1831 artwork.graphics_set_current_name = artwork.gfx_current->name;
1832 artwork.sounds_set_current_name = artwork.snd_current->name;
1833 artwork.music_set_current_name = artwork.mus_current->name;
1836 printf("graphics set == %s\n\n", artwork.graphics_set_current_name);
1837 printf("sounds set == %s\n\n", artwork.sounds_set_current_name);
1838 printf("music set == %s\n\n", artwork.music_set_current_name);
1841 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1842 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1843 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1846 dumpTreeInfo(artwork.gfx_first, 0);
1847 dumpTreeInfo(artwork.snd_first, 0);
1848 dumpTreeInfo(artwork.mus_first, 0);
1852 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1853 LevelDirTree *level_node)
1855 /* recursively check all level directories for artwork sub-directories */
1859 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1860 ARTWORK_DIRECTORY((*artwork_node)->type));
1863 if (!level_node->parent_link)
1864 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1865 level_node->filename, level_node->name);
1868 if (!level_node->parent_link)
1870 TreeInfo *topnode_last = *artwork_node;
1872 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1873 (*artwork_node)->type);
1875 if (topnode_last != *artwork_node)
1877 free((*artwork_node)->name);
1878 free((*artwork_node)->name_sorting);
1879 free((*artwork_node)->name_short);
1881 (*artwork_node)->name = getStringCopy(level_node->name);
1882 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1883 (*artwork_node)->name_short = getStringCopy(level_node->filename);
1885 (*artwork_node)->sort_priority = level_node->sort_priority;
1886 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1892 if (level_node->node_group != NULL)
1893 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1895 level_node = level_node->next;
1899 void LoadLevelArtworkInfo()
1901 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1903 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1904 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1905 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1907 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1908 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1909 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1912 dumpTreeInfo(artwork.gfx_first, 0);
1913 dumpTreeInfo(artwork.snd_first, 0);
1914 dumpTreeInfo(artwork.mus_first, 0);
1918 static void SaveUserLevelInfo()
1924 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
1926 if (!(file = fopen(filename, MODE_WRITE)))
1928 Error(ERR_WARN, "cannot write level info file '%s'", filename);
1933 /* always start with reliable default values */
1934 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
1936 ldi.name = getStringCopy(getLoginName());
1937 ldi.author = getStringCopy(getRealName());
1939 ldi.first_level = 1;
1940 ldi.sort_priority = LEVELCLASS_USER_START;
1941 ldi.readonly = FALSE;
1943 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1944 getCookie("LEVELINFO")));
1946 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1947 if (i != LEVELINFO_TOKEN_NAME_SHORT &&
1948 i != LEVELINFO_TOKEN_NAME_SORTING &&
1949 i != LEVELINFO_TOKEN_IMPORTED_FROM)
1950 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
1955 SetFilePermissions(filename, PERMS_PRIVATE);
1958 char *getSetupValue(int type, void *value)
1960 static char value_string[MAX_LINE_LEN];
1968 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
1972 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
1976 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
1980 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
1984 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
1988 sprintf(value_string, "%d", *(int *)value);
1992 strcpy(value_string, *(char **)value);
1996 value_string[0] = '\0';
2000 return value_string;
2003 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2007 static char token_string[MAX_LINE_LEN];
2008 int token_type = token_info[token_nr].type;
2009 void *setup_value = token_info[token_nr].value;
2010 char *token_text = token_info[token_nr].text;
2011 char *value_string = getSetupValue(token_type, setup_value);
2013 /* build complete token string */
2014 sprintf(token_string, "%s%s", prefix, token_text);
2016 /* build setup entry line */
2017 line = getFormattedSetupEntry(token_string, value_string);
2019 if (token_type == TYPE_KEY_X11)
2021 Key key = *(Key *)setup_value;
2022 char *keyname = getKeyNameFromKey(key);
2024 /* add comment, if useful */
2025 if (strcmp(keyname, "(undefined)") != 0 &&
2026 strcmp(keyname, "(unknown)") != 0)
2028 /* add at least one whitespace */
2030 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2034 strcat(line, keyname);
2041 void LoadLevelSetup_LastSeries()
2044 struct SetupFileList *level_setup_list = NULL;
2046 /* always start with reliable default values */
2047 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2049 /* ----------------------------------------------------------------------- */
2050 /* ~/.<program>/levelsetup.conf */
2051 /* ----------------------------------------------------------------------- */
2053 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2055 if ((level_setup_list = loadSetupFileList(filename)))
2057 char *last_level_series =
2058 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2060 leveldir_current = getTreeInfoFromFilename(leveldir_first,
2062 if (leveldir_current == NULL)
2063 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2065 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2067 freeSetupFileList(level_setup_list);
2070 Error(ERR_WARN, "using default setup values");
2075 void SaveLevelSetup_LastSeries()
2078 char *level_subdir = leveldir_current->filename;
2081 /* ----------------------------------------------------------------------- */
2082 /* ~/.<program>/levelsetup.conf */
2083 /* ----------------------------------------------------------------------- */
2085 InitUserDataDirectory();
2087 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2089 if (!(file = fopen(filename, MODE_WRITE)))
2091 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2096 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2097 getCookie("LEVELSETUP")));
2098 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2104 SetFilePermissions(filename, PERMS_PRIVATE);
2107 static void checkSeriesInfo()
2109 static char *level_directory = NULL;
2111 struct dirent *dir_entry;
2113 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2115 level_directory = getPath2((leveldir_current->user_defined ?
2116 getUserLevelDir(NULL) :
2117 options.level_directory),
2118 leveldir_current->fullpath);
2120 if ((dir = opendir(level_directory)) == NULL)
2122 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2126 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2128 if (strlen(dir_entry->d_name) > 4 &&
2129 dir_entry->d_name[3] == '.' &&
2130 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2132 char levelnum_str[4];
2135 strncpy(levelnum_str, dir_entry->d_name, 3);
2136 levelnum_str[3] = '\0';
2138 levelnum_value = atoi(levelnum_str);
2140 if (levelnum_value < leveldir_current->first_level)
2142 Error(ERR_WARN, "additional level %d found", levelnum_value);
2143 leveldir_current->first_level = levelnum_value;
2145 else if (levelnum_value > leveldir_current->last_level)
2147 Error(ERR_WARN, "additional level %d found", levelnum_value);
2148 leveldir_current->last_level = levelnum_value;
2156 void LoadLevelSetup_SeriesInfo()
2159 struct SetupFileList *level_setup_list = NULL;
2160 char *level_subdir = leveldir_current->filename;
2162 /* always start with reliable default values */
2163 level_nr = leveldir_current->first_level;
2165 checkSeriesInfo(leveldir_current);
2167 /* ----------------------------------------------------------------------- */
2168 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2169 /* ----------------------------------------------------------------------- */
2171 level_subdir = leveldir_current->filename;
2173 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2175 if ((level_setup_list = loadSetupFileList(filename)))
2179 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2183 level_nr = atoi(token_value);
2185 if (level_nr < leveldir_current->first_level)
2186 level_nr = leveldir_current->first_level;
2187 if (level_nr > leveldir_current->last_level)
2188 level_nr = leveldir_current->last_level;
2191 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2195 int level_nr = atoi(token_value);
2197 if (level_nr < leveldir_current->first_level)
2198 level_nr = leveldir_current->first_level;
2199 if (level_nr > leveldir_current->last_level + 1)
2200 level_nr = leveldir_current->last_level;
2202 if (leveldir_current->user_defined)
2203 level_nr = leveldir_current->last_level;
2205 leveldir_current->handicap_level = level_nr;
2208 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2210 freeSetupFileList(level_setup_list);
2213 Error(ERR_WARN, "using default setup values");
2218 void SaveLevelSetup_SeriesInfo()
2221 char *level_subdir = leveldir_current->filename;
2222 char *level_nr_str = int2str(level_nr, 0);
2223 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2226 /* ----------------------------------------------------------------------- */
2227 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2228 /* ----------------------------------------------------------------------- */
2230 InitLevelSetupDirectory(level_subdir);
2232 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2234 if (!(file = fopen(filename, MODE_WRITE)))
2236 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2241 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2242 getCookie("LEVELSETUP")));
2243 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2245 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2246 handicap_level_str));
2251 SetFilePermissions(filename, PERMS_PRIVATE);