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 = getCommonDataDir();
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 (msdos_filename != NULL)
429 free(msdos_filename);
431 msdos_filename = getStringCopy(basename_corrected);
432 strncpy(&msdos_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(getCommonDataDir(), "common data", PERMS_PUBLIC);
614 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
615 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
618 static void SaveUserLevelInfo();
620 void InitUserLevelDirectory(char *level_subdir)
622 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
624 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
625 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
626 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
632 void InitLevelSetupDirectory(char *level_subdir)
634 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
635 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
636 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
640 /* ------------------------------------------------------------------------- */
641 /* some functions to handle lists of level directories */
642 /* ------------------------------------------------------------------------- */
644 TreeInfo *newTreeInfo()
646 return checked_calloc(sizeof(TreeInfo));
649 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
651 node_new->next = *node_first;
652 *node_first = node_new;
655 int numTreeInfo(TreeInfo *node)
668 boolean validLevelSeries(TreeInfo *node)
670 return (node != NULL && !node->node_group && !node->parent_link);
673 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
678 if (node->node_group) /* enter level group (step down into tree) */
679 return getFirstValidTreeInfoEntry(node->node_group);
680 else if (node->parent_link) /* skip start entry of level group */
682 if (node->next) /* get first real level series entry */
683 return getFirstValidTreeInfoEntry(node->next);
684 else /* leave empty level group and go on */
685 return getFirstValidTreeInfoEntry(node->node_parent->next);
687 else /* this seems to be a regular level series */
691 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
696 if (node->node_parent == NULL) /* top level group */
697 return *node->node_top;
698 else /* sub level group */
699 return node->node_parent->node_group;
702 int numTreeInfoInGroup(TreeInfo *node)
704 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
707 int posTreeInfo(TreeInfo *node)
709 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
714 if (node_cmp == node)
718 node_cmp = node_cmp->next;
724 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
726 TreeInfo *node_default = node;
741 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
743 if (identifier == NULL)
748 if (node->node_group)
750 TreeInfo *node_group;
752 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
757 else if (!node->parent_link)
759 if (strcmp(identifier, node->identifier) == 0)
769 void dumpTreeInfo(TreeInfo *node, int depth)
773 printf("Dumping TreeInfo:\n");
777 for (i=0; i<(depth + 1) * 3; i++)
780 printf("filename == '%s' (%s) [%s] (%d)\n",
781 node->filename, node->name, node->identifier, node->sort_priority);
783 if (node->node_group != NULL)
784 dumpTreeInfo(node->node_group, depth + 1);
790 void sortTreeInfo(TreeInfo **node_first,
791 int (*compare_function)(const void *, const void *))
793 int num_nodes = numTreeInfo(*node_first);
794 TreeInfo **sort_array;
795 TreeInfo *node = *node_first;
801 /* allocate array for sorting structure pointers */
802 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
804 /* writing structure pointers to sorting array */
805 while (i < num_nodes && node) /* double boundary check... */
807 sort_array[i] = node;
813 /* sorting the structure pointers in the sorting array */
814 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
817 /* update the linkage of list elements with the sorted node array */
818 for (i=0; i<num_nodes - 1; i++)
819 sort_array[i]->next = sort_array[i + 1];
820 sort_array[num_nodes - 1]->next = NULL;
822 /* update the linkage of the main list anchor pointer */
823 *node_first = sort_array[0];
827 /* now recursively sort the level group structures */
831 if (node->node_group != NULL)
832 sortTreeInfo(&node->node_group, compare_function);
839 /* ========================================================================= */
840 /* some stuff from "files.c" */
841 /* ========================================================================= */
843 #if defined(PLATFORM_WIN32)
845 #define S_IRGRP S_IRUSR
848 #define S_IROTH S_IRUSR
851 #define S_IWGRP S_IWUSR
854 #define S_IWOTH S_IWUSR
857 #define S_IXGRP S_IXUSR
860 #define S_IXOTH S_IXUSR
863 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
868 #endif /* PLATFORM_WIN32 */
870 /* file permissions for newly written files */
871 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
872 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
873 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
875 #define MODE_W_PRIVATE (S_IWUSR)
876 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
877 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
879 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
880 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
882 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
883 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
885 char *getUserDataDir(void)
887 static char *userdata_dir = NULL;
889 if (userdata_dir == NULL)
890 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
895 char *getCommonDataDir(void)
897 static char *common_data_dir = NULL;
899 #if defined(PLATFORM_WIN32)
900 if (common_data_dir == NULL)
902 char *dir = checked_malloc(MAX_PATH + 1);
904 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
905 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
906 common_data_dir = getPath2(dir, program.userdata_directory);
908 common_data_dir = options.rw_base_directory;
911 if (common_data_dir == NULL)
912 common_data_dir = options.rw_base_directory;
915 return common_data_dir;
920 return getUserDataDir();
923 static mode_t posix_umask(mode_t mask)
925 #if defined(PLATFORM_UNIX)
932 static int posix_mkdir(const char *pathname, mode_t mode)
934 #if defined(PLATFORM_WIN32)
935 return mkdir(pathname);
937 return mkdir(pathname, mode);
941 void createDirectory(char *dir, char *text, int permission_class)
943 /* leave "other" permissions in umask untouched, but ensure group parts
944 of USERDATA_DIR_MODE are not masked */
945 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
946 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
947 mode_t normal_umask = posix_umask(0);
948 mode_t group_umask = ~(dir_mode & S_IRWXG);
949 posix_umask(normal_umask & group_umask);
951 if (access(dir, F_OK) != 0)
952 if (posix_mkdir(dir, dir_mode) != 0)
953 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
955 posix_umask(normal_umask); /* reset normal umask */
958 void InitUserDataDirectory()
960 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
963 void SetFilePermissions(char *filename, int permission_class)
965 chmod(filename, (permission_class == PERMS_PRIVATE ?
966 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
969 char *getCookie(char *file_type)
971 static char cookie[MAX_COOKIE_LEN + 1];
973 if (strlen(program.cookie_prefix) + 1 +
974 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
975 return "[COOKIE ERROR]"; /* should never happen */
977 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
978 program.cookie_prefix, file_type,
979 program.version_major, program.version_minor);
984 int getFileVersionFromCookieString(const char *cookie)
986 const char *ptr_cookie1, *ptr_cookie2;
987 const char *pattern1 = "_FILE_VERSION_";
988 const char *pattern2 = "?.?";
989 const int len_cookie = strlen(cookie);
990 const int len_pattern1 = strlen(pattern1);
991 const int len_pattern2 = strlen(pattern2);
992 const int len_pattern = len_pattern1 + len_pattern2;
993 int version_major, version_minor;
995 if (len_cookie <= len_pattern)
998 ptr_cookie1 = &cookie[len_cookie - len_pattern];
999 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
1001 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
1004 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
1005 ptr_cookie2[1] != '.' ||
1006 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1009 version_major = ptr_cookie2[0] - '0';
1010 version_minor = ptr_cookie2[2] - '0';
1012 return VERSION_IDENT(version_major, version_minor, 0);
1015 boolean checkCookieString(const char *cookie, const char *template)
1017 const char *pattern = "_FILE_VERSION_?.?";
1018 const int len_cookie = strlen(cookie);
1019 const int len_template = strlen(template);
1020 const int len_pattern = strlen(pattern);
1022 if (len_cookie != len_template)
1025 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1031 /* ------------------------------------------------------------------------- */
1032 /* setup file list handling functions */
1033 /* ------------------------------------------------------------------------- */
1035 char *getFormattedSetupEntry(char *token, char *value)
1038 static char entry[MAX_LINE_LEN];
1040 /* start with the token and some spaces to format output line */
1041 sprintf(entry, "%s:", token);
1042 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1045 /* continue with the token's value */
1046 strcat(entry, value);
1051 void freeSetupFileList(struct SetupFileList *setup_file_list)
1053 if (setup_file_list == NULL)
1056 if (setup_file_list->token)
1057 free(setup_file_list->token);
1058 if (setup_file_list->value)
1059 free(setup_file_list->value);
1060 if (setup_file_list->next)
1061 freeSetupFileList(setup_file_list->next);
1062 free(setup_file_list);
1065 struct SetupFileList *newSetupFileList(char *token, char *value)
1067 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1069 new->token = getStringCopy(token);
1070 new->value = getStringCopy(value);
1077 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1079 if (setup_file_list == NULL)
1082 if (strcmp(setup_file_list->token, token) == 0)
1083 return setup_file_list->value;
1085 return getTokenValue(setup_file_list->next, token);
1088 void setTokenValue(struct SetupFileList *setup_file_list,
1089 char *token, char *value)
1091 if (setup_file_list == NULL)
1094 if (strcmp(setup_file_list->token, token) == 0)
1096 if (setup_file_list->value)
1097 free(setup_file_list->value);
1099 setup_file_list->value = getStringCopy(value);
1101 else if (setup_file_list->next == NULL)
1102 setup_file_list->next = newSetupFileList(token, value);
1104 setTokenValue(setup_file_list->next, token, value);
1108 static void printSetupFileList(struct SetupFileList *setup_file_list)
1110 if (!setup_file_list)
1113 printf("token: '%s'\n", setup_file_list->token);
1114 printf("value: '%s'\n", setup_file_list->value);
1116 printSetupFileList(setup_file_list->next);
1120 struct SetupFileList *loadSetupFileList(char *filename)
1123 char line[MAX_LINE_LEN];
1124 char *token, *value, *line_ptr;
1125 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1126 struct SetupFileList *first_valid_list_entry;
1130 if (!(file = fopen(filename, MODE_READ)))
1132 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1138 /* read next line of input file */
1139 if (!fgets(line, MAX_LINE_LEN, file))
1142 /* cut trailing comment or whitespace from input line */
1143 for (line_ptr = line; *line_ptr; line_ptr++)
1145 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1152 /* cut trailing whitespaces from input line */
1153 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1154 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1157 /* ignore empty lines */
1161 line_len = strlen(line);
1163 /* cut leading whitespaces from token */
1164 for (token = line; *token; token++)
1165 if (*token != ' ' && *token != '\t')
1168 /* find end of token */
1169 for (line_ptr = token; *line_ptr; line_ptr++)
1171 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1178 if (line_ptr < line + line_len)
1179 value = line_ptr + 1;
1183 /* cut leading whitespaces from value */
1184 for (; *value; value++)
1185 if (*value != ' ' && *value != '\t')
1188 if (*token && *value)
1189 setTokenValue(setup_file_list, token, value);
1194 first_valid_list_entry = setup_file_list->next;
1196 /* free empty list header */
1197 setup_file_list->next = NULL;
1198 freeSetupFileList(setup_file_list);
1200 if (first_valid_list_entry == NULL)
1201 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1203 return first_valid_list_entry;
1206 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1209 if (!setup_file_list)
1212 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1214 if (!checkCookieString(setup_file_list->value, identifier))
1216 Error(ERR_WARN, "configuration file has wrong file identifier");
1223 if (setup_file_list->next)
1224 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1227 Error(ERR_WARN, "configuration file has no file identifier");
1233 /* ========================================================================= */
1234 /* setup file stuff */
1235 /* ========================================================================= */
1237 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1238 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1239 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1241 /* level directory info */
1242 #define LEVELINFO_TOKEN_IDENTIFIER 0
1243 #define LEVELINFO_TOKEN_NAME 1
1244 #define LEVELINFO_TOKEN_NAME_SORTING 2
1245 #define LEVELINFO_TOKEN_AUTHOR 3
1246 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1247 #define LEVELINFO_TOKEN_LEVELS 5
1248 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1249 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1250 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1251 #define LEVELINFO_TOKEN_READONLY 9
1252 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1253 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1254 #define LEVELINFO_TOKEN_MUSIC_SET 12
1256 #define NUM_LEVELINFO_TOKENS 13
1258 static LevelDirTree ldi;
1260 static struct TokenInfo levelinfo_tokens[] =
1262 /* level directory info */
1263 { TYPE_STRING, &ldi.identifier, "identifier" },
1264 { TYPE_STRING, &ldi.name, "name" },
1265 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1266 { TYPE_STRING, &ldi.author, "author" },
1267 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1268 { TYPE_INTEGER, &ldi.levels, "levels" },
1269 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1270 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1271 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1272 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1273 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1274 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1275 { TYPE_STRING, &ldi.music_set, "music_set" }
1278 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1282 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1283 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1284 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1285 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1288 ldi->node_parent = NULL;
1289 ldi->node_group = NULL;
1293 ldi->cl_cursor = -1;
1295 ldi->filename = NULL;
1296 ldi->fullpath = NULL;
1297 ldi->basepath = NULL;
1298 ldi->identifier = NULL;
1299 ldi->name = getStringCopy(ANONYMOUS_NAME);
1300 ldi->name_sorting = NULL;
1301 ldi->author = getStringCopy(ANONYMOUS_NAME);
1303 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1304 ldi->parent_link = FALSE;
1305 ldi->user_defined = FALSE;
1307 ldi->class_desc = NULL;
1309 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1311 ldi->imported_from = NULL;
1312 ldi->graphics_set = NULL;
1313 ldi->sounds_set = NULL;
1314 ldi->music_set = NULL;
1315 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1316 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1317 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1319 ldi->first_level = 0;
1320 ldi->last_level = 0;
1321 ldi->level_group = FALSE;
1322 ldi->handicap_level = 0;
1323 ldi->readonly = TRUE;
1327 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1331 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1333 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1337 /* first copy all values from the parent structure ... */
1340 /* ... then set all fields to default that cannot be inherited from parent.
1341 This is especially important for all those fields that can be set from
1342 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1343 calls 'free()' for all already set token values which requires that no
1344 other structure's pointer may point to them!
1347 ldi->filename = NULL;
1348 ldi->fullpath = NULL;
1349 ldi->basepath = NULL;
1350 ldi->identifier = NULL;
1351 ldi->name = getStringCopy(ANONYMOUS_NAME);
1352 ldi->name_sorting = NULL;
1353 ldi->author = getStringCopy(parent->author);
1354 ldi->imported_from = getStringCopy(parent->imported_from);
1356 ldi->level_group = FALSE;
1357 ldi->parent_link = FALSE;
1359 ldi->node_top = parent->node_top;
1360 ldi->node_parent = parent;
1361 ldi->node_group = NULL;
1365 void setSetupInfo(struct TokenInfo *token_info,
1366 int token_nr, char *token_value)
1368 int token_type = token_info[token_nr].type;
1369 void *setup_value = token_info[token_nr].value;
1371 if (token_value == NULL)
1374 /* set setup field to corresponding token value */
1379 *(boolean *)setup_value = get_boolean_from_string(token_value);
1383 *(Key *)setup_value = getKeyFromKeyName(token_value);
1387 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1391 *(int *)setup_value = get_integer_from_string(token_value);
1395 if (*(char **)setup_value != NULL)
1396 free(*(char **)setup_value);
1397 *(char **)setup_value = getStringCopy(token_value);
1405 static int compareTreeInfoEntries(const void *object1, const void *object2)
1407 const TreeInfo *entry1 = *((TreeInfo **)object1);
1408 const TreeInfo *entry2 = *((TreeInfo **)object2);
1409 int class_sorting1, class_sorting2;
1412 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1414 class_sorting1 = LEVELSORTING(entry1);
1415 class_sorting2 = LEVELSORTING(entry2);
1419 class_sorting1 = ARTWORKSORTING(entry1);
1420 class_sorting2 = ARTWORKSORTING(entry2);
1423 if (entry1->parent_link || entry2->parent_link)
1424 compare_result = (entry1->parent_link ? -1 : +1);
1425 else if (entry1->sort_priority == entry2->sort_priority)
1427 char *name1 = getStringToLower(entry1->name_sorting);
1428 char *name2 = getStringToLower(entry2->name_sorting);
1430 compare_result = strcmp(name1, name2);
1435 else if (class_sorting1 == class_sorting2)
1436 compare_result = entry1->sort_priority - entry2->sort_priority;
1438 compare_result = class_sorting1 - class_sorting2;
1440 return compare_result;
1443 static void createParentTreeInfoNode(TreeInfo *node_parent)
1447 if (node_parent == NULL)
1450 ti_new = newTreeInfo();
1451 setTreeInfoToDefaults(ti_new, node_parent->type);
1453 ti_new->node_parent = node_parent;
1454 ti_new->parent_link = TRUE;
1456 ti_new->identifier = getStringCopy(node_parent->identifier);
1457 ti_new->name = ".. (parent directory)";
1458 ti_new->name_sorting = getStringCopy(ti_new->name);
1460 ti_new->filename = "..";
1461 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1463 ti_new->sort_priority = node_parent->sort_priority;
1464 ti_new->class_desc = getLevelClassDescription(ti_new);
1466 pushTreeInfo(&node_parent->node_group, ti_new);
1469 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1470 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1472 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1473 TreeInfo *node_parent,
1474 char *level_directory,
1475 char *directory_name)
1477 char *directory_path = getPath2(level_directory, directory_name);
1478 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1479 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1480 LevelDirTree *leveldir_new = NULL;
1483 if (setup_file_list == NULL)
1485 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1487 free(directory_path);
1493 leveldir_new = newTreeInfo();
1496 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1498 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1500 leveldir_new->filename = getStringCopy(directory_name);
1502 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1504 /* set all structure fields according to the token/value pairs */
1505 ldi = *leveldir_new;
1506 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1507 setSetupInfo(levelinfo_tokens, i,
1508 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1509 *leveldir_new = ldi;
1511 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1513 free(leveldir_new->name);
1514 leveldir_new->name = getStringCopy(leveldir_new->filename);
1517 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1519 if (leveldir_new->identifier == NULL)
1520 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1522 if (leveldir_new->name_sorting == NULL)
1523 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1525 if (node_parent == NULL) /* top level group */
1527 leveldir_new->basepath = level_directory;
1528 leveldir_new->fullpath = leveldir_new->filename;
1530 else /* sub level group */
1532 leveldir_new->basepath = node_parent->basepath;
1533 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1536 if (leveldir_new->levels < 1)
1537 leveldir_new->levels = 1;
1539 leveldir_new->last_level =
1540 leveldir_new->first_level + leveldir_new->levels - 1;
1542 leveldir_new->user_defined =
1543 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1545 leveldir_new->color = LEVELCOLOR(leveldir_new);
1546 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1548 leveldir_new->handicap_level = /* set handicap to default value */
1549 (leveldir_new->user_defined ?
1550 leveldir_new->last_level :
1551 leveldir_new->first_level);
1553 pushTreeInfo(node_first, leveldir_new);
1555 freeSetupFileList(setup_file_list);
1557 if (leveldir_new->level_group)
1559 /* create node to link back to current level directory */
1560 createParentTreeInfoNode(leveldir_new);
1562 /* step into sub-directory and look for more level series */
1563 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1564 leveldir_new, directory_path);
1567 free(directory_path);
1573 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1574 TreeInfo *node_parent,
1575 char *level_directory)
1578 struct dirent *dir_entry;
1579 boolean valid_entry_found = FALSE;
1581 if ((dir = opendir(level_directory)) == NULL)
1583 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1587 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1589 struct stat file_status;
1590 char *directory_name = dir_entry->d_name;
1591 char *directory_path = getPath2(level_directory, directory_name);
1593 /* skip entries for current and parent directory */
1594 if (strcmp(directory_name, ".") == 0 ||
1595 strcmp(directory_name, "..") == 0)
1597 free(directory_path);
1601 /* find out if directory entry is itself a directory */
1602 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1603 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1605 free(directory_path);
1609 free(directory_path);
1611 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1612 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1613 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1616 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1623 if (!valid_entry_found)
1625 /* check if this directory directly contains a file "levelinfo.conf" */
1626 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1627 level_directory, ".");
1630 if (!valid_entry_found)
1631 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1635 void LoadLevelInfo()
1637 InitUserLevelDirectory(getLoginName());
1639 DrawInitText("Loading level series:", 120, FC_GREEN);
1641 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1642 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1644 /* before sorting, the first entries will be from the user directory */
1645 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1647 if (leveldir_first == NULL)
1648 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1650 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1653 dumpTreeInfo(leveldir_first, 0);
1657 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1658 TreeInfo *node_parent,
1659 char *base_directory,
1660 char *directory_name, int type)
1662 char *directory_path = getPath2(base_directory, directory_name);
1663 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1664 struct SetupFileList *setup_file_list = NULL;
1665 TreeInfo *artwork_new = NULL;
1668 if (access(filename, F_OK) == 0) /* file exists */
1669 setup_file_list = loadSetupFileList(filename);
1671 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1674 struct dirent *dir_entry;
1675 boolean valid_file_found = FALSE;
1677 if ((dir = opendir(directory_path)) != NULL)
1679 while ((dir_entry = readdir(dir)) != NULL)
1681 char *entry_name = dir_entry->d_name;
1683 if (FileIsArtworkType(entry_name, type))
1685 valid_file_found = TRUE;
1693 if (!valid_file_found)
1695 if (strcmp(directory_name, ".") != 0)
1696 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1698 free(directory_path);
1705 artwork_new = newTreeInfo();
1708 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1710 setTreeInfoToDefaults(artwork_new, type);
1712 artwork_new->filename = getStringCopy(directory_name);
1714 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1717 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1720 /* set all structure fields according to the token/value pairs */
1722 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1723 setSetupInfo(levelinfo_tokens, i,
1724 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1727 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1729 free(artwork_new->name);
1730 artwork_new->name = getStringCopy(artwork_new->filename);
1734 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1737 if (artwork_new->identifier == NULL)
1738 artwork_new->identifier = getStringCopy(artwork_new->filename);
1740 if (artwork_new->name_sorting == NULL)
1741 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1744 if (node_parent == NULL) /* top level group */
1746 artwork_new->basepath = getStringCopy(base_directory);
1747 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1749 else /* sub level group */
1751 artwork_new->basepath = getStringCopy(node_parent->basepath);
1752 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1755 artwork_new->user_defined =
1756 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1758 /* (may use ".sort_priority" from "setup_file_list" above) */
1759 artwork_new->color = ARTWORKCOLOR(artwork_new);
1760 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1762 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1764 if (artwork_new->name != NULL)
1765 free(artwork_new->name);
1767 if (strcmp(artwork_new->filename, ".") == 0)
1769 if (artwork_new->user_defined)
1771 artwork_new->identifier = getStringCopy("private");
1772 artwork_new->sort_priority = ARTWORKCLASS_USER;
1776 artwork_new->identifier = getStringCopy("classic");
1777 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1780 /* set to new values after changing ".sort_priority" */
1781 artwork_new->color = ARTWORKCOLOR(artwork_new);
1782 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1786 artwork_new->identifier = getStringCopy(artwork_new->filename);
1789 artwork_new->name = getStringCopy(artwork_new->identifier);
1790 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1793 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1795 pushTreeInfo(node_first, artwork_new);
1797 freeSetupFileList(setup_file_list);
1799 free(directory_path);
1805 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1806 TreeInfo *node_parent,
1807 char *base_directory, int type)
1810 struct dirent *dir_entry;
1811 boolean valid_entry_found = FALSE;
1813 if ((dir = opendir(base_directory)) == NULL)
1815 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1816 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1820 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1822 struct stat file_status;
1823 char *directory_name = dir_entry->d_name;
1824 char *directory_path = getPath2(base_directory, directory_name);
1826 /* skip entries for current and parent directory */
1827 if (strcmp(directory_name, ".") == 0 ||
1828 strcmp(directory_name, "..") == 0)
1830 free(directory_path);
1834 /* find out if directory entry is itself a directory */
1835 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1836 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1838 free(directory_path);
1842 free(directory_path);
1844 /* check if this directory contains artwork with or without config file */
1845 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1847 directory_name, type);
1852 /* check if this directory directly contains artwork itself */
1853 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1854 base_directory, ".",
1856 if (!valid_entry_found)
1857 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1861 static TreeInfo *getDummyArtworkInfo(int type)
1863 /* this is only needed when there is completely no artwork available */
1864 TreeInfo *artwork_new = newTreeInfo();
1866 setTreeInfoToDefaults(artwork_new, type);
1868 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1869 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1870 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1872 if (artwork_new->name != NULL)
1873 free(artwork_new->name);
1875 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1876 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1877 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1882 void LoadArtworkInfo()
1884 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1886 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1887 options.graphics_directory,
1888 TREE_TYPE_GRAPHICS_DIR);
1889 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1890 getUserGraphicsDir(),
1891 TREE_TYPE_GRAPHICS_DIR);
1893 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1894 options.sounds_directory,
1895 TREE_TYPE_SOUNDS_DIR);
1896 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1898 TREE_TYPE_SOUNDS_DIR);
1900 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1901 options.music_directory,
1902 TREE_TYPE_MUSIC_DIR);
1903 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1905 TREE_TYPE_MUSIC_DIR);
1907 if (artwork.gfx_first == NULL)
1908 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1909 if (artwork.snd_first == NULL)
1910 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1911 if (artwork.mus_first == NULL)
1912 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1914 /* before sorting, the first entries will be from the user directory */
1915 artwork.gfx_current =
1916 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1917 if (artwork.gfx_current == NULL)
1918 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1920 artwork.snd_current =
1921 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1922 if (artwork.snd_current == NULL)
1923 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1925 artwork.mus_current =
1926 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1927 if (artwork.mus_current == NULL)
1928 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1930 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1931 artwork.snd_current_identifier = artwork.snd_current->identifier;
1932 artwork.mus_current_identifier = artwork.mus_current->identifier;
1935 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1936 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1937 printf("music set == %s\n\n", artwork.mus_current_identifier);
1940 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1941 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1942 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1945 dumpTreeInfo(artwork.gfx_first, 0);
1946 dumpTreeInfo(artwork.snd_first, 0);
1947 dumpTreeInfo(artwork.mus_first, 0);
1951 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1952 LevelDirTree *level_node)
1954 /* recursively check all level directories for artwork sub-directories */
1958 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1959 ARTWORK_DIRECTORY((*artwork_node)->type));
1962 if (!level_node->parent_link)
1963 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1964 level_node->filename, level_node->name);
1967 if (!level_node->parent_link)
1969 TreeInfo *topnode_last = *artwork_node;
1971 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1972 (*artwork_node)->type);
1974 if (topnode_last != *artwork_node)
1976 free((*artwork_node)->identifier);
1977 free((*artwork_node)->name);
1978 free((*artwork_node)->name_sorting);
1980 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1981 (*artwork_node)->name = getStringCopy(level_node->name);
1982 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1984 (*artwork_node)->sort_priority = level_node->sort_priority;
1985 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1991 if (level_node->node_group != NULL)
1992 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1994 level_node = level_node->next;
1998 void LoadLevelArtworkInfo()
2000 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
2002 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
2003 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
2004 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
2006 /* needed for reloading level artwork not known at ealier stage */
2007 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
2009 artwork.gfx_current =
2010 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2011 if (artwork.gfx_current == NULL)
2012 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2015 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2017 artwork.snd_current =
2018 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2019 if (artwork.snd_current == NULL)
2020 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2023 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2025 artwork.mus_current =
2026 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2027 if (artwork.mus_current == NULL)
2028 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2031 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2032 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2033 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2036 dumpTreeInfo(artwork.gfx_first, 0);
2037 dumpTreeInfo(artwork.snd_first, 0);
2038 dumpTreeInfo(artwork.mus_first, 0);
2042 static void SaveUserLevelInfo()
2048 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2050 if (!(file = fopen(filename, MODE_WRITE)))
2052 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2057 /* always start with reliable default values */
2058 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2060 ldi.name = getStringCopy(getLoginName());
2061 ldi.author = getStringCopy(getRealName());
2063 ldi.first_level = 1;
2064 ldi.sort_priority = LEVELCLASS_USER_START;
2065 ldi.readonly = FALSE;
2066 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2067 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2068 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2070 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2071 getCookie("LEVELINFO")));
2073 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2074 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2075 i != LEVELINFO_TOKEN_NAME_SORTING &&
2076 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2077 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2082 SetFilePermissions(filename, PERMS_PRIVATE);
2085 char *getSetupValue(int type, void *value)
2087 static char value_string[MAX_LINE_LEN];
2095 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2099 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2103 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2107 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2111 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2115 sprintf(value_string, "%d", *(int *)value);
2119 strcpy(value_string, *(char **)value);
2123 value_string[0] = '\0';
2127 return value_string;
2130 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2134 static char token_string[MAX_LINE_LEN];
2135 int token_type = token_info[token_nr].type;
2136 void *setup_value = token_info[token_nr].value;
2137 char *token_text = token_info[token_nr].text;
2138 char *value_string = getSetupValue(token_type, setup_value);
2140 /* build complete token string */
2141 sprintf(token_string, "%s%s", prefix, token_text);
2143 /* build setup entry line */
2144 line = getFormattedSetupEntry(token_string, value_string);
2146 if (token_type == TYPE_KEY_X11)
2148 Key key = *(Key *)setup_value;
2149 char *keyname = getKeyNameFromKey(key);
2151 /* add comment, if useful */
2152 if (strcmp(keyname, "(undefined)") != 0 &&
2153 strcmp(keyname, "(unknown)") != 0)
2155 /* add at least one whitespace */
2157 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2161 strcat(line, keyname);
2168 void LoadLevelSetup_LastSeries()
2171 struct SetupFileList *level_setup_list = NULL;
2173 /* always start with reliable default values */
2174 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2176 /* ----------------------------------------------------------------------- */
2177 /* ~/.<program>/levelsetup.conf */
2178 /* ----------------------------------------------------------------------- */
2180 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2182 if ((level_setup_list = loadSetupFileList(filename)))
2184 char *last_level_series =
2185 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2187 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2189 if (leveldir_current == NULL)
2190 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2192 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2194 freeSetupFileList(level_setup_list);
2197 Error(ERR_WARN, "using default setup values");
2202 void SaveLevelSetup_LastSeries()
2205 char *level_subdir = leveldir_current->filename;
2208 /* ----------------------------------------------------------------------- */
2209 /* ~/.<program>/levelsetup.conf */
2210 /* ----------------------------------------------------------------------- */
2212 InitUserDataDirectory();
2214 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2216 if (!(file = fopen(filename, MODE_WRITE)))
2218 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2223 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2224 getCookie("LEVELSETUP")));
2225 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2231 SetFilePermissions(filename, PERMS_PRIVATE);
2234 static void checkSeriesInfo()
2236 static char *level_directory = NULL;
2238 struct dirent *dir_entry;
2240 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2242 level_directory = getPath2((leveldir_current->user_defined ?
2243 getUserLevelDir(NULL) :
2244 options.level_directory),
2245 leveldir_current->fullpath);
2247 if ((dir = opendir(level_directory)) == NULL)
2249 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2253 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2255 if (strlen(dir_entry->d_name) > 4 &&
2256 dir_entry->d_name[3] == '.' &&
2257 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2259 char levelnum_str[4];
2262 strncpy(levelnum_str, dir_entry->d_name, 3);
2263 levelnum_str[3] = '\0';
2265 levelnum_value = atoi(levelnum_str);
2267 if (levelnum_value < leveldir_current->first_level)
2269 Error(ERR_WARN, "additional level %d found", levelnum_value);
2270 leveldir_current->first_level = levelnum_value;
2272 else if (levelnum_value > leveldir_current->last_level)
2274 Error(ERR_WARN, "additional level %d found", levelnum_value);
2275 leveldir_current->last_level = levelnum_value;
2283 void LoadLevelSetup_SeriesInfo()
2286 struct SetupFileList *level_setup_list = NULL;
2287 char *level_subdir = leveldir_current->filename;
2289 /* always start with reliable default values */
2290 level_nr = leveldir_current->first_level;
2292 checkSeriesInfo(leveldir_current);
2294 /* ----------------------------------------------------------------------- */
2295 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2296 /* ----------------------------------------------------------------------- */
2298 level_subdir = leveldir_current->filename;
2300 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2302 if ((level_setup_list = loadSetupFileList(filename)))
2306 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2310 level_nr = atoi(token_value);
2312 if (level_nr < leveldir_current->first_level)
2313 level_nr = leveldir_current->first_level;
2314 if (level_nr > leveldir_current->last_level)
2315 level_nr = leveldir_current->last_level;
2318 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2322 int level_nr = atoi(token_value);
2324 if (level_nr < leveldir_current->first_level)
2325 level_nr = leveldir_current->first_level;
2326 if (level_nr > leveldir_current->last_level + 1)
2327 level_nr = leveldir_current->last_level;
2329 if (leveldir_current->user_defined)
2330 level_nr = leveldir_current->last_level;
2332 leveldir_current->handicap_level = level_nr;
2335 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2337 freeSetupFileList(level_setup_list);
2340 Error(ERR_WARN, "using default setup values");
2345 void SaveLevelSetup_SeriesInfo()
2348 char *level_subdir = leveldir_current->filename;
2349 char *level_nr_str = int2str(level_nr, 0);
2350 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2353 /* ----------------------------------------------------------------------- */
2354 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2355 /* ----------------------------------------------------------------------- */
2357 InitLevelSetupDirectory(level_subdir);
2359 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2361 if (!(file = fopen(filename, MODE_WRITE)))
2363 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2368 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2369 getCookie("LEVELSETUP")));
2370 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2372 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2373 handicap_level_str));
2378 SetFilePermissions(filename, PERMS_PRIVATE);