1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
25 /* file names and filename extensions */
26 #if !defined(PLATFORM_MSDOS)
27 #define LEVELSETUP_DIRECTORY "levelsetup"
28 #define SETUP_FILENAME "setup.conf"
29 #define LEVELSETUP_FILENAME "levelsetup.conf"
30 #define LEVELINFO_FILENAME "levelinfo.conf"
31 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
32 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
33 #define MUSICINFO_FILENAME "musicinfo.conf"
34 #define LEVELFILE_EXTENSION "level"
35 #define TAPEFILE_EXTENSION "tape"
36 #define SCOREFILE_EXTENSION "score"
38 #define LEVELSETUP_DIRECTORY "lvlsetup"
39 #define SETUP_FILENAME "setup.cnf"
40 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
41 #define LEVELINFO_FILENAME "lvlinfo.cnf"
42 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
43 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
44 #define MUSICINFO_FILENAME "musinfo.cnf"
45 #define LEVELFILE_EXTENSION "lvl"
46 #define TAPEFILE_EXTENSION "tap"
47 #define SCOREFILE_EXTENSION "sco"
50 #define NUM_LEVELCLASS_DESC 8
51 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
63 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
64 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
65 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
66 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
67 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
68 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
69 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
70 IS_LEVELCLASS_USER(n) ? FC_RED : \
73 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
74 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
75 IS_LEVELCLASS_BD(n) ? 2 : \
76 IS_LEVELCLASS_EM(n) ? 3 : \
77 IS_LEVELCLASS_SP(n) ? 4 : \
78 IS_LEVELCLASS_DX(n) ? 5 : \
79 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
80 IS_LEVELCLASS_USER(n) ? 7 : \
83 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
84 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
85 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
86 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
89 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
90 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
91 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
92 IS_ARTWORKCLASS_USER(n) ? 3 : \
95 #define TOKEN_VALUE_POSITION 40
96 #define TOKEN_COMMENT_POSITION 60
98 #define MAX_COOKIE_LEN 256
100 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
101 GRAPHICSINFO_FILENAME : \
102 (type) == TREE_TYPE_SOUNDS_DIR ? \
103 SOUNDSINFO_FILENAME : \
104 (type) == TREE_TYPE_MUSIC_DIR ? \
105 MUSICINFO_FILENAME : "")
107 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
108 GRAPHICS_DIRECTORY : \
109 (type) == TREE_TYPE_SOUNDS_DIR ? \
111 (type) == TREE_TYPE_MUSIC_DIR ? \
112 MUSIC_DIRECTORY : "")
114 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
115 options.graphics_directory : \
116 (type) == TREE_TYPE_SOUNDS_DIR ? \
117 options.sounds_directory : \
118 (type) == TREE_TYPE_MUSIC_DIR ? \
119 options.music_directory : "")
122 /* ------------------------------------------------------------------------- */
124 /* ------------------------------------------------------------------------- */
126 static char *getLevelClassDescription(TreeInfo *ldi)
128 int position = ldi->sort_priority / 100;
130 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
131 return levelclass_desc[position];
133 return "Unknown Level Class";
136 static char *getUserLevelDir(char *level_subdir)
138 static char *userlevel_dir = NULL;
139 char *data_dir = getUserDataDir();
140 char *userlevel_subdir = LEVELS_DIRECTORY;
145 if (level_subdir != NULL)
146 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
148 userlevel_dir = getPath2(data_dir, userlevel_subdir);
150 return userlevel_dir;
153 static char *getTapeDir(char *level_subdir)
155 static char *tape_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *tape_subdir = TAPES_DIRECTORY;
162 if (level_subdir != NULL)
163 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
165 tape_dir = getPath2(data_dir, tape_subdir);
170 static char *getScoreDir(char *level_subdir)
172 static char *score_dir = NULL;
173 char *data_dir = options.rw_base_directory;
174 char *score_subdir = SCORES_DIRECTORY;
179 if (level_subdir != NULL)
180 score_dir = getPath3(data_dir, score_subdir, level_subdir);
182 score_dir = getPath2(data_dir, score_subdir);
187 static char *getLevelSetupDir(char *level_subdir)
189 static char *levelsetup_dir = NULL;
190 char *data_dir = getUserDataDir();
191 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
194 free(levelsetup_dir);
196 if (level_subdir != NULL)
197 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
199 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
201 return levelsetup_dir;
204 static char *getLevelDirFromTreeInfo(TreeInfo *node)
206 static char *level_dir = NULL;
209 return options.level_directory;
214 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
215 options.level_directory), node->fullpath);
220 static char *getCurrentLevelDir()
222 return getLevelDirFromTreeInfo(leveldir_current);
225 static char *getDefaultGraphicsDir(char *graphics_subdir)
227 static char *graphics_dir = NULL;
229 if (graphics_subdir == NULL)
230 return options.graphics_directory;
235 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
240 static char *getDefaultSoundsDir(char *sounds_subdir)
242 static char *sounds_dir = NULL;
244 if (sounds_subdir == NULL)
245 return options.sounds_directory;
250 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
255 static char *getDefaultMusicDir(char *music_subdir)
257 static char *music_dir = NULL;
259 if (music_subdir == NULL)
260 return options.music_directory;
265 music_dir = getPath2(options.music_directory, music_subdir);
270 static char *getUserGraphicsDir()
272 static char *usergraphics_dir = NULL;
274 if (usergraphics_dir == NULL)
275 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
277 return usergraphics_dir;
280 static char *getUserSoundsDir()
282 static char *usersounds_dir = NULL;
284 if (usersounds_dir == NULL)
285 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
287 return usersounds_dir;
290 static char *getUserMusicDir()
292 static char *usermusic_dir = NULL;
294 if (usermusic_dir == NULL)
295 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
297 return usermusic_dir;
300 static char *getSetupArtworkDir(TreeInfo *ti)
302 static char *artwork_dir = NULL;
304 if (artwork_dir != NULL)
307 artwork_dir = getPath2(ti->basepath, ti->fullpath);
312 void setLevelArtworkDir(TreeInfo *ti)
314 char **artwork_path_ptr, *artwork_set;
315 TreeInfo *level_artwork;
317 if (ti == NULL || leveldir_current == NULL)
321 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
322 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
323 &leveldir_current->music_path);
326 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
327 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
328 leveldir_current->music_set);
330 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)) == NULL)
333 if (*artwork_path_ptr != NULL)
334 free(*artwork_path_ptr);
336 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
339 static char *getLevelArtworkDir(int type)
343 if (leveldir_current == NULL)
344 return UNDEFINED_FILENAME;
347 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
348 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
349 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
355 char *getLevelFilename(int nr)
357 static char *filename = NULL;
358 char basename[MAX_FILENAME_LEN];
360 if (filename != NULL)
363 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
364 filename = getPath2(getCurrentLevelDir(), basename);
369 char *getTapeFilename(int nr)
371 static char *filename = NULL;
372 char basename[MAX_FILENAME_LEN];
374 if (filename != NULL)
377 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
378 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
383 char *getScoreFilename(int nr)
385 static char *filename = NULL;
386 char basename[MAX_FILENAME_LEN];
388 if (filename != NULL)
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 if (filename != NULL)
404 filename = getPath2(getSetupDir(), SETUP_FILENAME);
409 static char *getCorrectedImageBasename(char *basename)
411 char *basename_corrected = basename;
413 #if defined(PLATFORM_MSDOS)
414 if (program.filename_prefix != NULL)
416 int prefix_len = strlen(program.filename_prefix);
418 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
419 basename_corrected = &basename[prefix_len];
421 /* if corrected filename is still longer than standard MS-DOS filename
422 size (8 characters + 1 dot + 3 characters file extension), shorten
423 filename by writing file extension after 8th basename character */
424 if (strlen(basename_corrected) > 8+1+3)
426 static char *msdos_filename = NULL;
428 if (filename != NULL)
431 filename = getStringCopy(basename_corrected);
432 strncpy(&filename[8], &basename[strlen(basename) - 1+3], 1+3 + 1);
437 return basename_corrected;
440 static boolean fileExists(char *filename)
443 printf("checking file '%s'\n", filename);
446 return (access(filename, F_OK) == 0);
449 char *getCustomImageFilename(char *basename)
451 static char *filename = NULL;
453 if (filename != NULL)
456 basename = getCorrectedImageBasename(basename);
458 if (!setup.override_level_graphics)
460 /* 1st try: look for special artwork configured in level series config */
461 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
462 if (fileExists(filename))
467 /* 2nd try: look for special artwork in current level series directory */
468 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
469 if (fileExists(filename))
475 /* 3rd try: look for special artwork in configured artwork directory */
476 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
477 if (fileExists(filename))
482 /* 4th try: look for default artwork in new default artwork directory */
483 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
484 if (fileExists(filename))
489 /* 5th try: look for default artwork in old default artwork directory */
490 filename = getPath2(options.graphics_directory, basename);
491 if (fileExists(filename))
494 return NULL; /* cannot find specified artwork file anywhere */
497 char *getCustomSoundFilename(char *basename)
499 static char *filename = NULL;
501 if (filename != NULL)
504 if (!setup.override_level_sounds)
506 /* 1st try: look for special artwork configured in level series config */
507 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
508 if (fileExists(filename))
513 /* 2nd try: look for special artwork in current level series directory */
514 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
515 if (fileExists(filename))
521 /* 3rd try: look for special artwork in configured artwork directory */
522 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
523 if (fileExists(filename))
528 /* 4th try: look for default artwork in new default artwork directory */
529 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
530 if (fileExists(filename))
535 /* 5th try: look for default artwork in old default artwork directory */
536 filename = getPath2(options.sounds_directory, basename);
537 if (fileExists(filename))
540 return NULL; /* cannot find specified artwork file anywhere */
543 char *getCustomArtworkFilename(char *basename, int type)
545 if (type == ARTWORK_TYPE_GRAPHICS)
546 return getCustomImageFilename(basename);
547 else if (type == ARTWORK_TYPE_SOUNDS)
548 return getCustomSoundFilename(basename);
550 return UNDEFINED_FILENAME;
553 char *getCustomArtworkConfigFilename(int type)
555 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
558 char *getCustomMusicDirectory(void)
560 static char *directory = NULL;
562 if (directory != NULL)
565 if (!setup.override_level_music)
567 /* 1st try: look for special artwork configured in level series config */
568 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
569 if (fileExists(directory))
574 /* 2nd try: look for special artwork in current level series directory */
575 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
576 if (fileExists(directory))
582 /* 3rd try: look for special artwork in configured artwork directory */
583 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
584 if (fileExists(directory))
589 /* 4th try: look for default artwork in new default artwork directory */
590 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
591 if (fileExists(directory))
596 /* 5th try: look for default artwork in old default artwork directory */
597 directory = getStringCopy(options.music_directory);
598 if (fileExists(directory))
601 return NULL; /* cannot find specified artwork file anywhere */
604 void InitTapeDirectory(char *level_subdir)
606 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
607 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
608 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
611 void InitScoreDirectory(char *level_subdir)
613 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
614 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
617 static void SaveUserLevelInfo();
619 void InitUserLevelDirectory(char *level_subdir)
621 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
623 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
624 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
625 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
631 void InitLevelSetupDirectory(char *level_subdir)
633 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
634 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
635 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
639 /* ------------------------------------------------------------------------- */
640 /* some functions to handle lists of level directories */
641 /* ------------------------------------------------------------------------- */
643 TreeInfo *newTreeInfo()
645 return checked_calloc(sizeof(TreeInfo));
648 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
650 node_new->next = *node_first;
651 *node_first = node_new;
654 int numTreeInfo(TreeInfo *node)
667 boolean validLevelSeries(TreeInfo *node)
669 return (node != NULL && !node->node_group && !node->parent_link);
672 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
677 if (node->node_group) /* enter level group (step down into tree) */
678 return getFirstValidTreeInfoEntry(node->node_group);
679 else if (node->parent_link) /* skip start entry of level group */
681 if (node->next) /* get first real level series entry */
682 return getFirstValidTreeInfoEntry(node->next);
683 else /* leave empty level group and go on */
684 return getFirstValidTreeInfoEntry(node->node_parent->next);
686 else /* this seems to be a regular level series */
690 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
695 if (node->node_parent == NULL) /* top level group */
696 return *node->node_top;
697 else /* sub level group */
698 return node->node_parent->node_group;
701 int numTreeInfoInGroup(TreeInfo *node)
703 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
706 int posTreeInfo(TreeInfo *node)
708 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
713 if (node_cmp == node)
717 node_cmp = node_cmp->next;
723 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
725 TreeInfo *node_default = node;
740 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
742 if (identifier == NULL)
747 if (node->node_group)
749 TreeInfo *node_group;
751 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
756 else if (!node->parent_link)
758 if (strcmp(identifier, node->identifier) == 0)
768 void dumpTreeInfo(TreeInfo *node, int depth)
772 printf("Dumping TreeInfo:\n");
776 for (i=0; i<(depth + 1) * 3; i++)
779 printf("filename == '%s' (%s) [%s] (%d)\n",
780 node->filename, node->name, node->identifier, node->sort_priority);
782 if (node->node_group != NULL)
783 dumpTreeInfo(node->node_group, depth + 1);
789 void sortTreeInfo(TreeInfo **node_first,
790 int (*compare_function)(const void *, const void *))
792 int num_nodes = numTreeInfo(*node_first);
793 TreeInfo **sort_array;
794 TreeInfo *node = *node_first;
800 /* allocate array for sorting structure pointers */
801 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
803 /* writing structure pointers to sorting array */
804 while (i < num_nodes && node) /* double boundary check... */
806 sort_array[i] = node;
812 /* sorting the structure pointers in the sorting array */
813 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
816 /* update the linkage of list elements with the sorted node array */
817 for (i=0; i<num_nodes - 1; i++)
818 sort_array[i]->next = sort_array[i + 1];
819 sort_array[num_nodes - 1]->next = NULL;
821 /* update the linkage of the main list anchor pointer */
822 *node_first = sort_array[0];
826 /* now recursively sort the level group structures */
830 if (node->node_group != NULL)
831 sortTreeInfo(&node->node_group, compare_function);
838 /* ========================================================================= */
839 /* some stuff from "files.c" */
840 /* ========================================================================= */
842 #if defined(PLATFORM_WIN32)
844 #define S_IRGRP S_IRUSR
847 #define S_IROTH S_IRUSR
850 #define S_IWGRP S_IWUSR
853 #define S_IWOTH S_IWUSR
856 #define S_IXGRP S_IXUSR
859 #define S_IXOTH S_IXUSR
862 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
867 #endif /* PLATFORM_WIN32 */
869 /* file permissions for newly written files */
870 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
871 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
872 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
874 #define MODE_W_PRIVATE (S_IWUSR)
875 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
876 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
878 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
879 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
881 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
882 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
884 char *getUserDataDir(void)
886 static char *userdata_dir = NULL;
890 char *home_dir = getHomeDir();
891 char *data_dir = program.userdata_directory;
893 userdata_dir = getPath2(home_dir, data_dir);
901 return getUserDataDir();
904 static mode_t posix_umask(mode_t mask)
906 #if defined(PLATFORM_UNIX)
913 static int posix_mkdir(const char *pathname, mode_t mode)
915 #if defined(PLATFORM_WIN32)
916 return mkdir(pathname);
918 return mkdir(pathname, mode);
922 void createDirectory(char *dir, char *text, int permission_class)
924 /* leave "other" permissions in umask untouched, but ensure group parts
925 of USERDATA_DIR_MODE are not masked */
926 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
927 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
928 mode_t normal_umask = posix_umask(0);
929 mode_t group_umask = ~(dir_mode & S_IRWXG);
930 posix_umask(normal_umask & group_umask);
932 if (access(dir, F_OK) != 0)
933 if (posix_mkdir(dir, dir_mode) != 0)
934 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
936 posix_umask(normal_umask); /* reset normal umask */
939 void InitUserDataDirectory()
941 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
944 void SetFilePermissions(char *filename, int permission_class)
946 chmod(filename, (permission_class == PERMS_PRIVATE ?
947 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
950 char *getCookie(char *file_type)
952 static char cookie[MAX_COOKIE_LEN + 1];
954 if (strlen(program.cookie_prefix) + 1 +
955 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
956 return "[COOKIE ERROR]"; /* should never happen */
958 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
959 program.cookie_prefix, file_type,
960 program.version_major, program.version_minor);
965 int getFileVersionFromCookieString(const char *cookie)
967 const char *ptr_cookie1, *ptr_cookie2;
968 const char *pattern1 = "_FILE_VERSION_";
969 const char *pattern2 = "?.?";
970 const int len_cookie = strlen(cookie);
971 const int len_pattern1 = strlen(pattern1);
972 const int len_pattern2 = strlen(pattern2);
973 const int len_pattern = len_pattern1 + len_pattern2;
974 int version_major, version_minor;
976 if (len_cookie <= len_pattern)
979 ptr_cookie1 = &cookie[len_cookie - len_pattern];
980 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
982 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
985 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
986 ptr_cookie2[1] != '.' ||
987 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
990 version_major = ptr_cookie2[0] - '0';
991 version_minor = ptr_cookie2[2] - '0';
993 return VERSION_IDENT(version_major, version_minor, 0);
996 boolean checkCookieString(const char *cookie, const char *template)
998 const char *pattern = "_FILE_VERSION_?.?";
999 const int len_cookie = strlen(cookie);
1000 const int len_template = strlen(template);
1001 const int len_pattern = strlen(pattern);
1003 if (len_cookie != len_template)
1006 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1012 /* ------------------------------------------------------------------------- */
1013 /* setup file list handling functions */
1014 /* ------------------------------------------------------------------------- */
1016 int get_string_integer_value(char *s)
1018 static char *number_text[][3] =
1020 { "0", "zero", "null", },
1021 { "1", "one", "first" },
1022 { "2", "two", "second" },
1023 { "3", "three", "third" },
1024 { "4", "four", "fourth" },
1025 { "5", "five", "fifth" },
1026 { "6", "six", "sixth" },
1027 { "7", "seven", "seventh" },
1028 { "8", "eight", "eighth" },
1029 { "9", "nine", "ninth" },
1030 { "10", "ten", "tenth" },
1031 { "11", "eleven", "eleventh" },
1032 { "12", "twelve", "twelfth" },
1036 char *s_lower = getStringToLower(s);
1039 for (i=0; i<13; i++)
1041 if (strcmp(s_lower, number_text[i][j]) == 0)
1052 boolean get_string_boolean_value(char *s)
1054 char *s_lower = getStringToLower(s);
1055 boolean result = FALSE;
1057 if (strcmp(s_lower, "true") == 0 ||
1058 strcmp(s_lower, "yes") == 0 ||
1059 strcmp(s_lower, "on") == 0 ||
1060 get_string_integer_value(s) == 1)
1068 char *getFormattedSetupEntry(char *token, char *value)
1071 static char entry[MAX_LINE_LEN];
1073 /* start with the token and some spaces to format output line */
1074 sprintf(entry, "%s:", token);
1075 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1078 /* continue with the token's value */
1079 strcat(entry, value);
1084 void freeSetupFileList(struct SetupFileList *setup_file_list)
1086 if (!setup_file_list)
1089 if (setup_file_list->token)
1090 free(setup_file_list->token);
1091 if (setup_file_list->value)
1092 free(setup_file_list->value);
1093 if (setup_file_list->next)
1094 freeSetupFileList(setup_file_list->next);
1095 free(setup_file_list);
1098 static struct SetupFileList *newSetupFileList(char *token, char *value)
1100 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1102 new->token = checked_malloc(strlen(token) + 1);
1103 strcpy(new->token, token);
1105 new->value = checked_malloc(strlen(value) + 1);
1106 strcpy(new->value, value);
1113 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1115 if (!setup_file_list)
1118 if (strcmp(setup_file_list->token, token) == 0)
1119 return setup_file_list->value;
1121 return getTokenValue(setup_file_list->next, token);
1124 static void setTokenValue(struct SetupFileList *setup_file_list,
1125 char *token, char *value)
1127 if (!setup_file_list)
1130 if (strcmp(setup_file_list->token, token) == 0)
1132 free(setup_file_list->value);
1133 setup_file_list->value = checked_malloc(strlen(value) + 1);
1134 strcpy(setup_file_list->value, value);
1136 else if (setup_file_list->next == NULL)
1137 setup_file_list->next = newSetupFileList(token, value);
1139 setTokenValue(setup_file_list->next, token, value);
1143 static void printSetupFileList(struct SetupFileList *setup_file_list)
1145 if (!setup_file_list)
1148 printf("token: '%s'\n", setup_file_list->token);
1149 printf("value: '%s'\n", setup_file_list->value);
1151 printSetupFileList(setup_file_list->next);
1155 struct SetupFileList *loadSetupFileList(char *filename)
1158 char line[MAX_LINE_LEN];
1159 char *token, *value, *line_ptr;
1160 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1161 struct SetupFileList *first_valid_list_entry;
1165 if (!(file = fopen(filename, MODE_READ)))
1167 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1173 /* read next line of input file */
1174 if (!fgets(line, MAX_LINE_LEN, file))
1177 /* cut trailing comment or whitespace from input line */
1178 for (line_ptr = line; *line_ptr; line_ptr++)
1180 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1187 /* cut trailing whitespaces from input line */
1188 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1189 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1192 /* ignore empty lines */
1196 line_len = strlen(line);
1198 /* cut leading whitespaces from token */
1199 for (token = line; *token; token++)
1200 if (*token != ' ' && *token != '\t')
1203 /* find end of token */
1204 for (line_ptr = token; *line_ptr; line_ptr++)
1206 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1213 if (line_ptr < line + line_len)
1214 value = line_ptr + 1;
1218 /* cut leading whitespaces from value */
1219 for (; *value; value++)
1220 if (*value != ' ' && *value != '\t')
1223 if (*token && *value)
1224 setTokenValue(setup_file_list, token, value);
1229 first_valid_list_entry = setup_file_list->next;
1231 /* free empty list header */
1232 setup_file_list->next = NULL;
1233 freeSetupFileList(setup_file_list);
1235 if (first_valid_list_entry == NULL)
1236 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1238 return first_valid_list_entry;
1241 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1244 if (!setup_file_list)
1247 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1249 if (!checkCookieString(setup_file_list->value, identifier))
1251 Error(ERR_WARN, "configuration file has wrong file identifier");
1258 if (setup_file_list->next)
1259 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1262 Error(ERR_WARN, "configuration file has no file identifier");
1268 /* ========================================================================= */
1269 /* setup file stuff */
1270 /* ========================================================================= */
1272 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1273 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1274 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1276 /* level directory info */
1277 #define LEVELINFO_TOKEN_IDENTIFIER 0
1278 #define LEVELINFO_TOKEN_NAME 1
1279 #define LEVELINFO_TOKEN_NAME_SORTING 2
1280 #define LEVELINFO_TOKEN_AUTHOR 3
1281 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1282 #define LEVELINFO_TOKEN_LEVELS 5
1283 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1284 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1285 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1286 #define LEVELINFO_TOKEN_READONLY 9
1287 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1288 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1289 #define LEVELINFO_TOKEN_MUSIC_SET 12
1291 #define NUM_LEVELINFO_TOKENS 13
1293 static LevelDirTree ldi;
1295 static struct TokenInfo levelinfo_tokens[] =
1297 /* level directory info */
1298 { TYPE_STRING, &ldi.identifier, "identifier" },
1299 { TYPE_STRING, &ldi.name, "name" },
1300 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1301 { TYPE_STRING, &ldi.author, "author" },
1302 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1303 { TYPE_INTEGER, &ldi.levels, "levels" },
1304 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1305 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1306 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1307 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1308 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1309 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1310 { TYPE_STRING, &ldi.music_set, "music_set" }
1313 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1317 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1318 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1319 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1320 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1323 ldi->node_parent = NULL;
1324 ldi->node_group = NULL;
1328 ldi->cl_cursor = -1;
1330 ldi->filename = NULL;
1331 ldi->fullpath = NULL;
1332 ldi->basepath = NULL;
1333 ldi->identifier = NULL;
1334 ldi->name = getStringCopy(ANONYMOUS_NAME);
1335 ldi->name_sorting = NULL;
1336 ldi->author = getStringCopy(ANONYMOUS_NAME);
1338 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1339 ldi->parent_link = FALSE;
1340 ldi->user_defined = FALSE;
1342 ldi->class_desc = NULL;
1344 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1346 ldi->imported_from = NULL;
1347 ldi->graphics_set = NULL;
1348 ldi->sounds_set = NULL;
1349 ldi->music_set = NULL;
1350 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1351 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1352 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1354 ldi->first_level = 0;
1355 ldi->last_level = 0;
1356 ldi->level_group = FALSE;
1357 ldi->handicap_level = 0;
1358 ldi->readonly = TRUE;
1362 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1366 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1368 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1372 /* first copy all values from the parent structure ... */
1375 /* ... then set all fields to default that cannot be inherited from parent.
1376 This is especially important for all those fields that can be set from
1377 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1378 calls 'free()' for all already set token values which requires that no
1379 other structure's pointer may point to them!
1382 ldi->filename = NULL;
1383 ldi->fullpath = NULL;
1384 ldi->basepath = NULL;
1385 ldi->identifier = NULL;
1386 ldi->name = getStringCopy(ANONYMOUS_NAME);
1387 ldi->name_sorting = NULL;
1388 ldi->author = getStringCopy(parent->author);
1389 ldi->imported_from = getStringCopy(parent->imported_from);
1391 ldi->level_group = FALSE;
1392 ldi->parent_link = FALSE;
1394 ldi->node_top = parent->node_top;
1395 ldi->node_parent = parent;
1396 ldi->node_group = NULL;
1400 void setSetupInfo(struct TokenInfo *token_info,
1401 int token_nr, char *token_value)
1403 int token_type = token_info[token_nr].type;
1404 void *setup_value = token_info[token_nr].value;
1406 if (token_value == NULL)
1409 /* set setup field to corresponding token value */
1414 *(boolean *)setup_value = get_string_boolean_value(token_value);
1418 *(Key *)setup_value = getKeyFromKeyName(token_value);
1422 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1426 *(int *)setup_value = get_string_integer_value(token_value);
1430 if (*(char **)setup_value != NULL)
1431 free(*(char **)setup_value);
1432 *(char **)setup_value = getStringCopy(token_value);
1440 static int compareTreeInfoEntries(const void *object1, const void *object2)
1442 const TreeInfo *entry1 = *((TreeInfo **)object1);
1443 const TreeInfo *entry2 = *((TreeInfo **)object2);
1444 int class_sorting1, class_sorting2;
1447 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1449 class_sorting1 = LEVELSORTING(entry1);
1450 class_sorting2 = LEVELSORTING(entry2);
1454 class_sorting1 = ARTWORKSORTING(entry1);
1455 class_sorting2 = ARTWORKSORTING(entry2);
1458 if (entry1->parent_link || entry2->parent_link)
1459 compare_result = (entry1->parent_link ? -1 : +1);
1460 else if (entry1->sort_priority == entry2->sort_priority)
1462 char *name1 = getStringToLower(entry1->name_sorting);
1463 char *name2 = getStringToLower(entry2->name_sorting);
1465 compare_result = strcmp(name1, name2);
1470 else if (class_sorting1 == class_sorting2)
1471 compare_result = entry1->sort_priority - entry2->sort_priority;
1473 compare_result = class_sorting1 - class_sorting2;
1475 return compare_result;
1478 static void createParentTreeInfoNode(TreeInfo *node_parent)
1482 if (node_parent == NULL)
1485 ti_new = newTreeInfo();
1486 setTreeInfoToDefaults(ti_new, node_parent->type);
1488 ti_new->node_parent = node_parent;
1489 ti_new->parent_link = TRUE;
1491 ti_new->identifier = getStringCopy(node_parent->identifier);
1492 ti_new->name = ".. (parent directory)";
1493 ti_new->name_sorting = getStringCopy(ti_new->name);
1495 ti_new->filename = "..";
1496 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1498 ti_new->sort_priority = node_parent->sort_priority;
1499 ti_new->class_desc = getLevelClassDescription(ti_new);
1501 pushTreeInfo(&node_parent->node_group, ti_new);
1504 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1505 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1507 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1508 TreeInfo *node_parent,
1509 char *level_directory,
1510 char *directory_name)
1512 char *directory_path = getPath2(level_directory, directory_name);
1513 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1514 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1515 LevelDirTree *leveldir_new = NULL;
1518 if (setup_file_list == NULL)
1520 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1522 free(directory_path);
1528 leveldir_new = newTreeInfo();
1531 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1533 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1535 leveldir_new->filename = getStringCopy(directory_name);
1537 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1539 /* set all structure fields according to the token/value pairs */
1540 ldi = *leveldir_new;
1541 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1542 setSetupInfo(levelinfo_tokens, i,
1543 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1544 *leveldir_new = ldi;
1546 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1548 free(leveldir_new->name);
1549 leveldir_new->name = getStringCopy(leveldir_new->filename);
1552 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1554 if (leveldir_new->identifier == NULL)
1555 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1557 if (leveldir_new->name_sorting == NULL)
1558 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1560 if (node_parent == NULL) /* top level group */
1562 leveldir_new->basepath = level_directory;
1563 leveldir_new->fullpath = leveldir_new->filename;
1565 else /* sub level group */
1567 leveldir_new->basepath = node_parent->basepath;
1568 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1571 if (leveldir_new->levels < 1)
1572 leveldir_new->levels = 1;
1574 leveldir_new->last_level =
1575 leveldir_new->first_level + leveldir_new->levels - 1;
1577 leveldir_new->user_defined =
1578 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1580 leveldir_new->color = LEVELCOLOR(leveldir_new);
1581 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1583 leveldir_new->handicap_level = /* set handicap to default value */
1584 (leveldir_new->user_defined ?
1585 leveldir_new->last_level :
1586 leveldir_new->first_level);
1588 pushTreeInfo(node_first, leveldir_new);
1590 freeSetupFileList(setup_file_list);
1592 if (leveldir_new->level_group)
1594 /* create node to link back to current level directory */
1595 createParentTreeInfoNode(leveldir_new);
1597 /* step into sub-directory and look for more level series */
1598 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1599 leveldir_new, directory_path);
1602 free(directory_path);
1608 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1609 TreeInfo *node_parent,
1610 char *level_directory)
1613 struct dirent *dir_entry;
1614 boolean valid_entry_found = FALSE;
1616 if ((dir = opendir(level_directory)) == NULL)
1618 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1622 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1624 struct stat file_status;
1625 char *directory_name = dir_entry->d_name;
1626 char *directory_path = getPath2(level_directory, directory_name);
1628 /* skip entries for current and parent directory */
1629 if (strcmp(directory_name, ".") == 0 ||
1630 strcmp(directory_name, "..") == 0)
1632 free(directory_path);
1636 /* find out if directory entry is itself a directory */
1637 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1638 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1640 free(directory_path);
1644 free(directory_path);
1646 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1647 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1648 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1651 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1658 if (!valid_entry_found)
1660 /* check if this directory directly contains a file "levelinfo.conf" */
1661 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1662 level_directory, ".");
1665 if (!valid_entry_found)
1666 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1670 void LoadLevelInfo()
1672 InitUserLevelDirectory(getLoginName());
1674 DrawInitText("Loading level series:", 120, FC_GREEN);
1676 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1677 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1679 /* before sorting, the first entries will be from the user directory */
1680 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1682 if (leveldir_first == NULL)
1683 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1685 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1688 dumpTreeInfo(leveldir_first, 0);
1692 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1693 TreeInfo *node_parent,
1694 char *base_directory,
1695 char *directory_name, int type)
1697 char *directory_path = getPath2(base_directory, directory_name);
1698 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1699 struct SetupFileList *setup_file_list = NULL;
1700 TreeInfo *artwork_new = NULL;
1703 if (access(filename, F_OK) == 0) /* file exists */
1704 setup_file_list = loadSetupFileList(filename);
1706 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1709 struct dirent *dir_entry;
1710 boolean valid_file_found = FALSE;
1712 if ((dir = opendir(directory_path)) != NULL)
1714 while ((dir_entry = readdir(dir)) != NULL)
1716 char *entry_name = dir_entry->d_name;
1718 if (FileIsArtworkType(entry_name, type))
1720 valid_file_found = TRUE;
1728 if (!valid_file_found)
1730 if (strcmp(directory_name, ".") != 0)
1731 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1733 free(directory_path);
1740 artwork_new = newTreeInfo();
1743 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1745 setTreeInfoToDefaults(artwork_new, type);
1747 artwork_new->filename = getStringCopy(directory_name);
1749 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1752 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1755 /* set all structure fields according to the token/value pairs */
1757 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1758 setSetupInfo(levelinfo_tokens, i,
1759 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1762 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1764 free(artwork_new->name);
1765 artwork_new->name = getStringCopy(artwork_new->filename);
1769 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1772 if (artwork_new->identifier == NULL)
1773 artwork_new->identifier = getStringCopy(artwork_new->filename);
1775 if (artwork_new->name_sorting == NULL)
1776 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1779 if (node_parent == NULL) /* top level group */
1781 artwork_new->basepath = getStringCopy(base_directory);
1782 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1784 else /* sub level group */
1786 artwork_new->basepath = getStringCopy(node_parent->basepath);
1787 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1790 artwork_new->user_defined =
1791 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1793 /* (may use ".sort_priority" from "setup_file_list" above) */
1794 artwork_new->color = ARTWORKCOLOR(artwork_new);
1795 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1797 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1799 if (artwork_new->name != NULL)
1800 free(artwork_new->name);
1802 if (strcmp(artwork_new->filename, ".") == 0)
1804 if (artwork_new->user_defined)
1806 artwork_new->identifier = getStringCopy("private");
1807 artwork_new->sort_priority = ARTWORKCLASS_USER;
1811 artwork_new->identifier = getStringCopy("classic");
1812 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1815 /* set to new values after changing ".sort_priority" */
1816 artwork_new->color = ARTWORKCOLOR(artwork_new);
1817 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1821 artwork_new->identifier = getStringCopy(artwork_new->filename);
1824 artwork_new->name = getStringCopy(artwork_new->identifier);
1825 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1828 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1830 pushTreeInfo(node_first, artwork_new);
1832 freeSetupFileList(setup_file_list);
1834 free(directory_path);
1840 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1841 TreeInfo *node_parent,
1842 char *base_directory, int type)
1845 struct dirent *dir_entry;
1846 boolean valid_entry_found = FALSE;
1848 if ((dir = opendir(base_directory)) == NULL)
1850 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1851 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1855 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1857 struct stat file_status;
1858 char *directory_name = dir_entry->d_name;
1859 char *directory_path = getPath2(base_directory, directory_name);
1861 /* skip entries for current and parent directory */
1862 if (strcmp(directory_name, ".") == 0 ||
1863 strcmp(directory_name, "..") == 0)
1865 free(directory_path);
1869 /* find out if directory entry is itself a directory */
1870 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1871 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1873 free(directory_path);
1877 free(directory_path);
1879 /* check if this directory contains artwork with or without config file */
1880 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1882 directory_name, type);
1887 /* check if this directory directly contains artwork itself */
1888 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1889 base_directory, ".",
1891 if (!valid_entry_found)
1892 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1896 static TreeInfo *getDummyArtworkInfo(int type)
1898 /* this is only needed when there is completely no artwork available */
1899 TreeInfo *artwork_new = newTreeInfo();
1901 setTreeInfoToDefaults(artwork_new, type);
1903 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1904 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1905 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1907 if (artwork_new->name != NULL)
1908 free(artwork_new->name);
1910 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1911 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1912 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1917 void LoadArtworkInfo()
1919 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1921 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1922 options.graphics_directory,
1923 TREE_TYPE_GRAPHICS_DIR);
1924 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1925 getUserGraphicsDir(),
1926 TREE_TYPE_GRAPHICS_DIR);
1928 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1929 options.sounds_directory,
1930 TREE_TYPE_SOUNDS_DIR);
1931 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1933 TREE_TYPE_SOUNDS_DIR);
1935 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1936 options.music_directory,
1937 TREE_TYPE_MUSIC_DIR);
1938 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1940 TREE_TYPE_MUSIC_DIR);
1942 if (artwork.gfx_first == NULL)
1943 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1944 if (artwork.snd_first == NULL)
1945 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1946 if (artwork.mus_first == NULL)
1947 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1949 /* before sorting, the first entries will be from the user directory */
1950 artwork.gfx_current =
1951 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1952 if (artwork.gfx_current == NULL)
1953 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1955 artwork.snd_current =
1956 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1957 if (artwork.snd_current == NULL)
1958 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1960 artwork.mus_current =
1961 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1962 if (artwork.mus_current == NULL)
1963 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1965 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1966 artwork.snd_current_identifier = artwork.snd_current->identifier;
1967 artwork.mus_current_identifier = artwork.mus_current->identifier;
1970 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1971 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1972 printf("music set == %s\n\n", artwork.mus_current_identifier);
1975 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1976 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1977 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1980 dumpTreeInfo(artwork.gfx_first, 0);
1981 dumpTreeInfo(artwork.snd_first, 0);
1982 dumpTreeInfo(artwork.mus_first, 0);
1986 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1987 LevelDirTree *level_node)
1989 /* recursively check all level directories for artwork sub-directories */
1993 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1994 ARTWORK_DIRECTORY((*artwork_node)->type));
1997 if (!level_node->parent_link)
1998 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1999 level_node->filename, level_node->name);
2002 if (!level_node->parent_link)
2004 TreeInfo *topnode_last = *artwork_node;
2006 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
2007 (*artwork_node)->type);
2009 if (topnode_last != *artwork_node)
2011 free((*artwork_node)->identifier);
2012 free((*artwork_node)->name);
2013 free((*artwork_node)->name_sorting);
2015 (*artwork_node)->identifier = getStringCopy(level_node->filename);
2016 (*artwork_node)->name = getStringCopy(level_node->name);
2017 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
2019 (*artwork_node)->sort_priority = level_node->sort_priority;
2020 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
2026 if (level_node->node_group != NULL)
2027 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
2029 level_node = level_node->next;
2033 void LoadLevelArtworkInfo()
2035 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2037 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2038 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2039 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2041 /* needed for reloading level artwork not known at ealier stage */
2042 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2044 artwork.gfx_current =
2045 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2046 if (artwork.gfx_current == NULL)
2047 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2050 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2052 artwork.snd_current =
2053 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2054 if (artwork.snd_current == NULL)
2055 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2058 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2060 artwork.mus_current =
2061 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2062 if (artwork.mus_current == NULL)
2063 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2066 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2067 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2068 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2071 dumpTreeInfo(artwork.gfx_first, 0);
2072 dumpTreeInfo(artwork.snd_first, 0);
2073 dumpTreeInfo(artwork.mus_first, 0);
2077 static void SaveUserLevelInfo()
2083 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2085 if (!(file = fopen(filename, MODE_WRITE)))
2087 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2092 /* always start with reliable default values */
2093 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2095 ldi.name = getStringCopy(getLoginName());
2096 ldi.author = getStringCopy(getRealName());
2098 ldi.first_level = 1;
2099 ldi.sort_priority = LEVELCLASS_USER_START;
2100 ldi.readonly = FALSE;
2101 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2102 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2103 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2105 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2106 getCookie("LEVELINFO")));
2108 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2109 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2110 i != LEVELINFO_TOKEN_NAME_SORTING &&
2111 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2112 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2117 SetFilePermissions(filename, PERMS_PRIVATE);
2120 char *getSetupValue(int type, void *value)
2122 static char value_string[MAX_LINE_LEN];
2130 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2134 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2138 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2142 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2146 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2150 sprintf(value_string, "%d", *(int *)value);
2154 strcpy(value_string, *(char **)value);
2158 value_string[0] = '\0';
2162 return value_string;
2165 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2169 static char token_string[MAX_LINE_LEN];
2170 int token_type = token_info[token_nr].type;
2171 void *setup_value = token_info[token_nr].value;
2172 char *token_text = token_info[token_nr].text;
2173 char *value_string = getSetupValue(token_type, setup_value);
2175 /* build complete token string */
2176 sprintf(token_string, "%s%s", prefix, token_text);
2178 /* build setup entry line */
2179 line = getFormattedSetupEntry(token_string, value_string);
2181 if (token_type == TYPE_KEY_X11)
2183 Key key = *(Key *)setup_value;
2184 char *keyname = getKeyNameFromKey(key);
2186 /* add comment, if useful */
2187 if (strcmp(keyname, "(undefined)") != 0 &&
2188 strcmp(keyname, "(unknown)") != 0)
2190 /* add at least one whitespace */
2192 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2196 strcat(line, keyname);
2203 void LoadLevelSetup_LastSeries()
2206 struct SetupFileList *level_setup_list = NULL;
2208 /* always start with reliable default values */
2209 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2211 /* ----------------------------------------------------------------------- */
2212 /* ~/.<program>/levelsetup.conf */
2213 /* ----------------------------------------------------------------------- */
2215 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2217 if ((level_setup_list = loadSetupFileList(filename)))
2219 char *last_level_series =
2220 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2222 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2224 if (leveldir_current == NULL)
2225 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2227 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2229 freeSetupFileList(level_setup_list);
2232 Error(ERR_WARN, "using default setup values");
2237 void SaveLevelSetup_LastSeries()
2240 char *level_subdir = leveldir_current->filename;
2243 /* ----------------------------------------------------------------------- */
2244 /* ~/.<program>/levelsetup.conf */
2245 /* ----------------------------------------------------------------------- */
2247 InitUserDataDirectory();
2249 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2251 if (!(file = fopen(filename, MODE_WRITE)))
2253 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2258 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2259 getCookie("LEVELSETUP")));
2260 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2266 SetFilePermissions(filename, PERMS_PRIVATE);
2269 static void checkSeriesInfo()
2271 static char *level_directory = NULL;
2273 struct dirent *dir_entry;
2275 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2277 level_directory = getPath2((leveldir_current->user_defined ?
2278 getUserLevelDir(NULL) :
2279 options.level_directory),
2280 leveldir_current->fullpath);
2282 if ((dir = opendir(level_directory)) == NULL)
2284 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2288 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2290 if (strlen(dir_entry->d_name) > 4 &&
2291 dir_entry->d_name[3] == '.' &&
2292 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2294 char levelnum_str[4];
2297 strncpy(levelnum_str, dir_entry->d_name, 3);
2298 levelnum_str[3] = '\0';
2300 levelnum_value = atoi(levelnum_str);
2302 if (levelnum_value < leveldir_current->first_level)
2304 Error(ERR_WARN, "additional level %d found", levelnum_value);
2305 leveldir_current->first_level = levelnum_value;
2307 else if (levelnum_value > leveldir_current->last_level)
2309 Error(ERR_WARN, "additional level %d found", levelnum_value);
2310 leveldir_current->last_level = levelnum_value;
2318 void LoadLevelSetup_SeriesInfo()
2321 struct SetupFileList *level_setup_list = NULL;
2322 char *level_subdir = leveldir_current->filename;
2324 /* always start with reliable default values */
2325 level_nr = leveldir_current->first_level;
2327 checkSeriesInfo(leveldir_current);
2329 /* ----------------------------------------------------------------------- */
2330 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2331 /* ----------------------------------------------------------------------- */
2333 level_subdir = leveldir_current->filename;
2335 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2337 if ((level_setup_list = loadSetupFileList(filename)))
2341 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2345 level_nr = atoi(token_value);
2347 if (level_nr < leveldir_current->first_level)
2348 level_nr = leveldir_current->first_level;
2349 if (level_nr > leveldir_current->last_level)
2350 level_nr = leveldir_current->last_level;
2353 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2357 int level_nr = atoi(token_value);
2359 if (level_nr < leveldir_current->first_level)
2360 level_nr = leveldir_current->first_level;
2361 if (level_nr > leveldir_current->last_level + 1)
2362 level_nr = leveldir_current->last_level;
2364 if (leveldir_current->user_defined)
2365 level_nr = leveldir_current->last_level;
2367 leveldir_current->handicap_level = level_nr;
2370 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2372 freeSetupFileList(level_setup_list);
2375 Error(ERR_WARN, "using default setup values");
2380 void SaveLevelSetup_SeriesInfo()
2383 char *level_subdir = leveldir_current->filename;
2384 char *level_nr_str = int2str(level_nr, 0);
2385 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2388 /* ----------------------------------------------------------------------- */
2389 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2390 /* ----------------------------------------------------------------------- */
2392 InitLevelSetupDirectory(level_subdir);
2394 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2396 if (!(file = fopen(filename, MODE_WRITE)))
2398 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2403 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2404 getCookie("LEVELSETUP")));
2405 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2407 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2408 handicap_level_str));
2413 SetFilePermissions(filename, PERMS_PRIVATE);