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 char *getCustomImageFilename(char *basename)
442 static char *filename = NULL;
444 if (filename != NULL)
447 basename = getCorrectedImageBasename(basename);
449 if (!setup.override_level_graphics)
451 /* 1st try: look for special artwork configured in level series config */
452 filename = getPath2(getLevelArtworkDir(TREE_TYPE_GRAPHICS_DIR), basename);
453 if (fileExists(filename))
458 /* 2nd try: look for special artwork in current level series directory */
459 filename = getPath3(getCurrentLevelDir(), GRAPHICS_DIRECTORY, basename);
460 if (fileExists(filename))
466 /* 3rd try: look for special artwork in configured artwork directory */
467 filename = getPath2(getSetupArtworkDir(artwork.gfx_current), basename);
468 if (fileExists(filename))
473 /* 4th try: look for default artwork in new default artwork directory */
474 filename = getPath2(getDefaultGraphicsDir(GRAPHICS_SUBDIR), basename);
475 if (fileExists(filename))
480 /* 5th try: look for default artwork in old default artwork directory */
481 filename = getPath2(options.graphics_directory, basename);
482 if (fileExists(filename))
485 return NULL; /* cannot find specified artwork file anywhere */
488 char *getCustomSoundFilename(char *basename)
490 static char *filename = NULL;
492 if (filename != NULL)
495 if (!setup.override_level_sounds)
497 /* 1st try: look for special artwork configured in level series config */
498 filename = getPath2(getLevelArtworkDir(TREE_TYPE_SOUNDS_DIR), basename);
499 if (fileExists(filename))
504 /* 2nd try: look for special artwork in current level series directory */
505 filename = getPath3(getCurrentLevelDir(), SOUNDS_DIRECTORY, basename);
506 if (fileExists(filename))
512 /* 3rd try: look for special artwork in configured artwork directory */
513 filename = getPath2(getSetupArtworkDir(artwork.snd_current), basename);
514 if (fileExists(filename))
519 /* 4th try: look for default artwork in new default artwork directory */
520 filename = getPath2(getDefaultSoundsDir(SOUNDS_SUBDIR), basename);
521 if (fileExists(filename))
526 /* 5th try: look for default artwork in old default artwork directory */
527 filename = getPath2(options.sounds_directory, basename);
528 if (fileExists(filename))
531 return NULL; /* cannot find specified artwork file anywhere */
534 char *getCustomArtworkFilename(char *basename, int type)
536 if (type == ARTWORK_TYPE_GRAPHICS)
537 return getCustomImageFilename(basename);
538 else if (type == ARTWORK_TYPE_SOUNDS)
539 return getCustomSoundFilename(basename);
541 return UNDEFINED_FILENAME;
544 char *getCustomArtworkConfigFilename(int type)
546 return getCustomArtworkFilename(ARTWORKINFO_FILENAME(type), type);
549 char *getCustomMusicDirectory(void)
551 static char *directory = NULL;
553 if (directory != NULL)
556 if (!setup.override_level_music)
558 /* 1st try: look for special artwork configured in level series config */
559 directory = getStringCopy(getLevelArtworkDir(TREE_TYPE_MUSIC_DIR));
560 if (fileExists(directory))
565 /* 2nd try: look for special artwork in current level series directory */
566 directory = getPath2(getCurrentLevelDir(), MUSIC_DIRECTORY);
567 if (fileExists(directory))
573 /* 3rd try: look for special artwork in configured artwork directory */
574 directory = getStringCopy(getSetupArtworkDir(artwork.mus_current));
575 if (fileExists(directory))
580 /* 4th try: look for default artwork in new default artwork directory */
581 directory = getStringCopy(getDefaultMusicDir(MUSIC_SUBDIR));
582 if (fileExists(directory))
587 /* 5th try: look for default artwork in old default artwork directory */
588 directory = getStringCopy(options.music_directory);
589 if (fileExists(directory))
592 return NULL; /* cannot find specified artwork file anywhere */
595 void InitTapeDirectory(char *level_subdir)
597 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
598 createDirectory(getTapeDir(NULL), "main tape", PERMS_PRIVATE);
599 createDirectory(getTapeDir(level_subdir), "level tape", PERMS_PRIVATE);
602 void InitScoreDirectory(char *level_subdir)
604 createDirectory(getCommonDataDir(), "common data", PERMS_PUBLIC);
605 createDirectory(getScoreDir(NULL), "main score", PERMS_PUBLIC);
606 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
609 static void SaveUserLevelInfo();
611 void InitUserLevelDirectory(char *level_subdir)
613 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
615 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
616 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
617 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
623 void InitLevelSetupDirectory(char *level_subdir)
625 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
626 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
627 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
631 /* ------------------------------------------------------------------------- */
632 /* some functions to handle lists of level directories */
633 /* ------------------------------------------------------------------------- */
635 TreeInfo *newTreeInfo()
637 return checked_calloc(sizeof(TreeInfo));
640 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
642 node_new->next = *node_first;
643 *node_first = node_new;
646 int numTreeInfo(TreeInfo *node)
659 boolean validLevelSeries(TreeInfo *node)
661 return (node != NULL && !node->node_group && !node->parent_link);
664 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
669 if (node->node_group) /* enter level group (step down into tree) */
670 return getFirstValidTreeInfoEntry(node->node_group);
671 else if (node->parent_link) /* skip start entry of level group */
673 if (node->next) /* get first real level series entry */
674 return getFirstValidTreeInfoEntry(node->next);
675 else /* leave empty level group and go on */
676 return getFirstValidTreeInfoEntry(node->node_parent->next);
678 else /* this seems to be a regular level series */
682 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
687 if (node->node_parent == NULL) /* top level group */
688 return *node->node_top;
689 else /* sub level group */
690 return node->node_parent->node_group;
693 int numTreeInfoInGroup(TreeInfo *node)
695 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
698 int posTreeInfo(TreeInfo *node)
700 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
705 if (node_cmp == node)
709 node_cmp = node_cmp->next;
715 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
717 TreeInfo *node_default = node;
732 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
734 if (identifier == NULL)
739 if (node->node_group)
741 TreeInfo *node_group;
743 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
748 else if (!node->parent_link)
750 if (strcmp(identifier, node->identifier) == 0)
760 void dumpTreeInfo(TreeInfo *node, int depth)
764 printf("Dumping TreeInfo:\n");
768 for (i=0; i<(depth + 1) * 3; i++)
771 printf("filename == '%s' (%s) [%s] (%d)\n",
772 node->filename, node->name, node->identifier, node->sort_priority);
774 if (node->node_group != NULL)
775 dumpTreeInfo(node->node_group, depth + 1);
781 void sortTreeInfo(TreeInfo **node_first,
782 int (*compare_function)(const void *, const void *))
784 int num_nodes = numTreeInfo(*node_first);
785 TreeInfo **sort_array;
786 TreeInfo *node = *node_first;
792 /* allocate array for sorting structure pointers */
793 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
795 /* writing structure pointers to sorting array */
796 while (i < num_nodes && node) /* double boundary check... */
798 sort_array[i] = node;
804 /* sorting the structure pointers in the sorting array */
805 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
808 /* update the linkage of list elements with the sorted node array */
809 for (i=0; i<num_nodes - 1; i++)
810 sort_array[i]->next = sort_array[i + 1];
811 sort_array[num_nodes - 1]->next = NULL;
813 /* update the linkage of the main list anchor pointer */
814 *node_first = sort_array[0];
818 /* now recursively sort the level group structures */
822 if (node->node_group != NULL)
823 sortTreeInfo(&node->node_group, compare_function);
830 /* ========================================================================= */
831 /* some stuff from "files.c" */
832 /* ========================================================================= */
834 #if defined(PLATFORM_WIN32)
836 #define S_IRGRP S_IRUSR
839 #define S_IROTH S_IRUSR
842 #define S_IWGRP S_IWUSR
845 #define S_IWOTH S_IWUSR
848 #define S_IXGRP S_IXUSR
851 #define S_IXOTH S_IXUSR
854 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
859 #endif /* PLATFORM_WIN32 */
861 /* file permissions for newly written files */
862 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
863 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
864 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
866 #define MODE_W_PRIVATE (S_IWUSR)
867 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
868 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
870 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
871 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
873 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
874 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
876 char *getUserDataDir(void)
878 static char *userdata_dir = NULL;
880 if (userdata_dir == NULL)
881 userdata_dir = getPath2(getHomeDir(), program.userdata_directory);
886 char *getCommonDataDir(void)
888 static char *common_data_dir = NULL;
890 #if defined(PLATFORM_WIN32)
891 if (common_data_dir == NULL)
893 char *dir = checked_malloc(MAX_PATH + 1);
895 if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_DOCUMENTS, NULL, 0, dir))
896 && strcmp(dir, "") != 0) /* empty for Windows 95/98 */
897 common_data_dir = getPath2(dir, program.userdata_directory);
899 common_data_dir = options.rw_base_directory;
902 if (common_data_dir == NULL)
903 common_data_dir = options.rw_base_directory;
906 return common_data_dir;
911 return getUserDataDir();
914 static mode_t posix_umask(mode_t mask)
916 #if defined(PLATFORM_UNIX)
923 static int posix_mkdir(const char *pathname, mode_t mode)
925 #if defined(PLATFORM_WIN32)
926 return mkdir(pathname);
928 return mkdir(pathname, mode);
932 void createDirectory(char *dir, char *text, int permission_class)
934 /* leave "other" permissions in umask untouched, but ensure group parts
935 of USERDATA_DIR_MODE are not masked */
936 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
937 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
938 mode_t normal_umask = posix_umask(0);
939 mode_t group_umask = ~(dir_mode & S_IRWXG);
940 posix_umask(normal_umask & group_umask);
942 if (access(dir, F_OK) != 0)
943 if (posix_mkdir(dir, dir_mode) != 0)
944 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
946 posix_umask(normal_umask); /* reset normal umask */
949 void InitUserDataDirectory()
951 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
954 void SetFilePermissions(char *filename, int permission_class)
956 chmod(filename, (permission_class == PERMS_PRIVATE ?
957 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
960 char *getCookie(char *file_type)
962 static char cookie[MAX_COOKIE_LEN + 1];
964 if (strlen(program.cookie_prefix) + 1 +
965 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
966 return "[COOKIE ERROR]"; /* should never happen */
968 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
969 program.cookie_prefix, file_type,
970 program.version_major, program.version_minor);
975 int getFileVersionFromCookieString(const char *cookie)
977 const char *ptr_cookie1, *ptr_cookie2;
978 const char *pattern1 = "_FILE_VERSION_";
979 const char *pattern2 = "?.?";
980 const int len_cookie = strlen(cookie);
981 const int len_pattern1 = strlen(pattern1);
982 const int len_pattern2 = strlen(pattern2);
983 const int len_pattern = len_pattern1 + len_pattern2;
984 int version_major, version_minor;
986 if (len_cookie <= len_pattern)
989 ptr_cookie1 = &cookie[len_cookie - len_pattern];
990 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
992 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
995 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
996 ptr_cookie2[1] != '.' ||
997 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
1000 version_major = ptr_cookie2[0] - '0';
1001 version_minor = ptr_cookie2[2] - '0';
1003 return VERSION_IDENT(version_major, version_minor, 0);
1006 boolean checkCookieString(const char *cookie, const char *template)
1008 const char *pattern = "_FILE_VERSION_?.?";
1009 const int len_cookie = strlen(cookie);
1010 const int len_template = strlen(template);
1011 const int len_pattern = strlen(pattern);
1013 if (len_cookie != len_template)
1016 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1022 /* ------------------------------------------------------------------------- */
1023 /* setup file list handling functions */
1024 /* ------------------------------------------------------------------------- */
1026 char *getFormattedSetupEntry(char *token, char *value)
1029 static char entry[MAX_LINE_LEN];
1031 /* start with the token and some spaces to format output line */
1032 sprintf(entry, "%s:", token);
1033 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1036 /* continue with the token's value */
1037 strcat(entry, value);
1042 void freeSetupFileList(struct SetupFileList *setup_file_list)
1044 if (setup_file_list == NULL)
1047 if (setup_file_list->token)
1048 free(setup_file_list->token);
1049 if (setup_file_list->value)
1050 free(setup_file_list->value);
1051 if (setup_file_list->next)
1052 freeSetupFileList(setup_file_list->next);
1053 free(setup_file_list);
1056 struct SetupFileList *newSetupFileList(char *token, char *value)
1058 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1060 new->token = getStringCopy(token);
1061 new->value = getStringCopy(value);
1068 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1070 if (setup_file_list == NULL)
1073 if (strcmp(setup_file_list->token, token) == 0)
1074 return setup_file_list->value;
1076 return getTokenValue(setup_file_list->next, token);
1079 void setTokenValue(struct SetupFileList *setup_file_list,
1080 char *token, char *value)
1082 if (setup_file_list == NULL)
1085 if (strcmp(setup_file_list->token, token) == 0)
1087 if (setup_file_list->value)
1088 free(setup_file_list->value);
1090 setup_file_list->value = getStringCopy(value);
1092 else if (setup_file_list->next == NULL)
1093 setup_file_list->next = newSetupFileList(token, value);
1095 setTokenValue(setup_file_list->next, token, value);
1099 static void printSetupFileList(struct SetupFileList *setup_file_list)
1101 if (!setup_file_list)
1104 printf("token: '%s'\n", setup_file_list->token);
1105 printf("value: '%s'\n", setup_file_list->value);
1107 printSetupFileList(setup_file_list->next);
1111 struct SetupFileList *loadSetupFileList(char *filename)
1114 char line[MAX_LINE_LEN];
1115 char *token, *value, *line_ptr;
1116 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1117 struct SetupFileList *first_valid_list_entry;
1120 if (!(file = fopen(filename, MODE_READ)))
1122 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1128 /* read next line of input file */
1129 if (!fgets(line, MAX_LINE_LEN, file))
1132 /* cut trailing comment or whitespace from input line */
1133 for (line_ptr = line; *line_ptr; line_ptr++)
1135 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1142 /* cut trailing whitespaces from input line */
1143 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1144 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1147 /* ignore empty lines */
1151 line_len = strlen(line);
1153 /* cut leading whitespaces from token */
1154 for (token = line; *token; token++)
1155 if (*token != ' ' && *token != '\t')
1158 /* find end of token */
1159 for (line_ptr = token; *line_ptr; line_ptr++)
1161 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1168 if (line_ptr < line + line_len)
1169 value = line_ptr + 1;
1173 /* cut leading whitespaces from value */
1174 for (; *value; value++)
1175 if (*value != ' ' && *value != '\t')
1178 if (*token && *value)
1179 setTokenValue(setup_file_list, token, value);
1184 first_valid_list_entry = setup_file_list->next;
1186 /* free empty list header */
1187 setup_file_list->next = NULL;
1188 freeSetupFileList(setup_file_list);
1190 if (first_valid_list_entry == NULL)
1191 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1193 return first_valid_list_entry;
1196 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1199 if (!setup_file_list)
1202 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1204 if (!checkCookieString(setup_file_list->value, identifier))
1206 Error(ERR_WARN, "configuration file has wrong file identifier");
1213 if (setup_file_list->next)
1214 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1217 Error(ERR_WARN, "configuration file has no file identifier");
1223 /* ========================================================================= */
1224 /* setup file stuff */
1225 /* ========================================================================= */
1227 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1228 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1229 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1231 /* level directory info */
1232 #define LEVELINFO_TOKEN_IDENTIFIER 0
1233 #define LEVELINFO_TOKEN_NAME 1
1234 #define LEVELINFO_TOKEN_NAME_SORTING 2
1235 #define LEVELINFO_TOKEN_AUTHOR 3
1236 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1237 #define LEVELINFO_TOKEN_LEVELS 5
1238 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1239 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1240 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1241 #define LEVELINFO_TOKEN_READONLY 9
1242 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1243 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1244 #define LEVELINFO_TOKEN_MUSIC_SET 12
1246 #define NUM_LEVELINFO_TOKENS 13
1248 static LevelDirTree ldi;
1250 static struct TokenInfo levelinfo_tokens[] =
1252 /* level directory info */
1253 { TYPE_STRING, &ldi.identifier, "identifier" },
1254 { TYPE_STRING, &ldi.name, "name" },
1255 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1256 { TYPE_STRING, &ldi.author, "author" },
1257 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1258 { TYPE_INTEGER, &ldi.levels, "levels" },
1259 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1260 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1261 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1262 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1263 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1264 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1265 { TYPE_STRING, &ldi.music_set, "music_set" }
1268 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1272 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1273 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1274 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1275 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1278 ldi->node_parent = NULL;
1279 ldi->node_group = NULL;
1283 ldi->cl_cursor = -1;
1285 ldi->filename = NULL;
1286 ldi->fullpath = NULL;
1287 ldi->basepath = NULL;
1288 ldi->identifier = NULL;
1289 ldi->name = getStringCopy(ANONYMOUS_NAME);
1290 ldi->name_sorting = NULL;
1291 ldi->author = getStringCopy(ANONYMOUS_NAME);
1293 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1294 ldi->parent_link = FALSE;
1295 ldi->user_defined = FALSE;
1297 ldi->class_desc = NULL;
1299 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1301 ldi->imported_from = NULL;
1302 ldi->graphics_set = NULL;
1303 ldi->sounds_set = NULL;
1304 ldi->music_set = NULL;
1305 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1306 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1307 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1309 ldi->first_level = 0;
1310 ldi->last_level = 0;
1311 ldi->level_group = FALSE;
1312 ldi->handicap_level = 0;
1313 ldi->readonly = TRUE;
1317 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1321 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1323 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1327 /* first copy all values from the parent structure ... */
1330 /* ... then set all fields to default that cannot be inherited from parent.
1331 This is especially important for all those fields that can be set from
1332 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1333 calls 'free()' for all already set token values which requires that no
1334 other structure's pointer may point to them!
1337 ldi->filename = NULL;
1338 ldi->fullpath = NULL;
1339 ldi->basepath = NULL;
1340 ldi->identifier = NULL;
1341 ldi->name = getStringCopy(ANONYMOUS_NAME);
1342 ldi->name_sorting = NULL;
1343 ldi->author = getStringCopy(parent->author);
1344 ldi->imported_from = getStringCopy(parent->imported_from);
1346 ldi->level_group = FALSE;
1347 ldi->parent_link = FALSE;
1349 ldi->node_top = parent->node_top;
1350 ldi->node_parent = parent;
1351 ldi->node_group = NULL;
1355 void setSetupInfo(struct TokenInfo *token_info,
1356 int token_nr, char *token_value)
1358 int token_type = token_info[token_nr].type;
1359 void *setup_value = token_info[token_nr].value;
1361 if (token_value == NULL)
1364 /* set setup field to corresponding token value */
1369 *(boolean *)setup_value = get_boolean_from_string(token_value);
1373 *(Key *)setup_value = getKeyFromKeyName(token_value);
1377 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1381 *(int *)setup_value = get_integer_from_string(token_value);
1385 if (*(char **)setup_value != NULL)
1386 free(*(char **)setup_value);
1387 *(char **)setup_value = getStringCopy(token_value);
1395 static int compareTreeInfoEntries(const void *object1, const void *object2)
1397 const TreeInfo *entry1 = *((TreeInfo **)object1);
1398 const TreeInfo *entry2 = *((TreeInfo **)object2);
1399 int class_sorting1, class_sorting2;
1402 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1404 class_sorting1 = LEVELSORTING(entry1);
1405 class_sorting2 = LEVELSORTING(entry2);
1409 class_sorting1 = ARTWORKSORTING(entry1);
1410 class_sorting2 = ARTWORKSORTING(entry2);
1413 if (entry1->parent_link || entry2->parent_link)
1414 compare_result = (entry1->parent_link ? -1 : +1);
1415 else if (entry1->sort_priority == entry2->sort_priority)
1417 char *name1 = getStringToLower(entry1->name_sorting);
1418 char *name2 = getStringToLower(entry2->name_sorting);
1420 compare_result = strcmp(name1, name2);
1425 else if (class_sorting1 == class_sorting2)
1426 compare_result = entry1->sort_priority - entry2->sort_priority;
1428 compare_result = class_sorting1 - class_sorting2;
1430 return compare_result;
1433 static void createParentTreeInfoNode(TreeInfo *node_parent)
1437 if (node_parent == NULL)
1440 ti_new = newTreeInfo();
1441 setTreeInfoToDefaults(ti_new, node_parent->type);
1443 ti_new->node_parent = node_parent;
1444 ti_new->parent_link = TRUE;
1446 ti_new->identifier = getStringCopy(node_parent->identifier);
1447 ti_new->name = ".. (parent directory)";
1448 ti_new->name_sorting = getStringCopy(ti_new->name);
1450 ti_new->filename = "..";
1451 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1453 ti_new->sort_priority = node_parent->sort_priority;
1454 ti_new->class_desc = getLevelClassDescription(ti_new);
1456 pushTreeInfo(&node_parent->node_group, ti_new);
1459 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1460 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1462 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1463 TreeInfo *node_parent,
1464 char *level_directory,
1465 char *directory_name)
1467 char *directory_path = getPath2(level_directory, directory_name);
1468 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1469 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1470 LevelDirTree *leveldir_new = NULL;
1473 if (setup_file_list == NULL)
1475 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1477 free(directory_path);
1483 leveldir_new = newTreeInfo();
1486 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1488 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1490 leveldir_new->filename = getStringCopy(directory_name);
1492 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1494 /* set all structure fields according to the token/value pairs */
1495 ldi = *leveldir_new;
1496 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1497 setSetupInfo(levelinfo_tokens, i,
1498 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1499 *leveldir_new = ldi;
1501 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1503 free(leveldir_new->name);
1504 leveldir_new->name = getStringCopy(leveldir_new->filename);
1507 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1509 if (leveldir_new->identifier == NULL)
1510 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1512 if (leveldir_new->name_sorting == NULL)
1513 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1515 if (node_parent == NULL) /* top level group */
1517 leveldir_new->basepath = level_directory;
1518 leveldir_new->fullpath = leveldir_new->filename;
1520 else /* sub level group */
1522 leveldir_new->basepath = node_parent->basepath;
1523 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1526 if (leveldir_new->levels < 1)
1527 leveldir_new->levels = 1;
1529 leveldir_new->last_level =
1530 leveldir_new->first_level + leveldir_new->levels - 1;
1532 leveldir_new->user_defined =
1533 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1535 leveldir_new->color = LEVELCOLOR(leveldir_new);
1536 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1538 leveldir_new->handicap_level = /* set handicap to default value */
1539 (leveldir_new->user_defined ?
1540 leveldir_new->last_level :
1541 leveldir_new->first_level);
1543 pushTreeInfo(node_first, leveldir_new);
1545 freeSetupFileList(setup_file_list);
1547 if (leveldir_new->level_group)
1549 /* create node to link back to current level directory */
1550 createParentTreeInfoNode(leveldir_new);
1552 /* step into sub-directory and look for more level series */
1553 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1554 leveldir_new, directory_path);
1557 free(directory_path);
1563 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1564 TreeInfo *node_parent,
1565 char *level_directory)
1568 struct dirent *dir_entry;
1569 boolean valid_entry_found = FALSE;
1571 if ((dir = opendir(level_directory)) == NULL)
1573 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1577 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1579 struct stat file_status;
1580 char *directory_name = dir_entry->d_name;
1581 char *directory_path = getPath2(level_directory, directory_name);
1583 /* skip entries for current and parent directory */
1584 if (strcmp(directory_name, ".") == 0 ||
1585 strcmp(directory_name, "..") == 0)
1587 free(directory_path);
1591 /* find out if directory entry is itself a directory */
1592 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1593 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1595 free(directory_path);
1599 free(directory_path);
1601 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1602 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1603 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1606 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1613 if (!valid_entry_found)
1615 /* check if this directory directly contains a file "levelinfo.conf" */
1616 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1617 level_directory, ".");
1620 if (!valid_entry_found)
1621 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1625 void LoadLevelInfo()
1627 InitUserLevelDirectory(getLoginName());
1629 DrawInitText("Loading level series:", 120, FC_GREEN);
1631 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1632 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1634 /* before sorting, the first entries will be from the user directory */
1635 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1637 if (leveldir_first == NULL)
1638 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1640 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1643 dumpTreeInfo(leveldir_first, 0);
1647 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1648 TreeInfo *node_parent,
1649 char *base_directory,
1650 char *directory_name, int type)
1652 char *directory_path = getPath2(base_directory, directory_name);
1653 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1654 struct SetupFileList *setup_file_list = NULL;
1655 TreeInfo *artwork_new = NULL;
1658 if (access(filename, F_OK) == 0) /* file exists */
1659 setup_file_list = loadSetupFileList(filename);
1661 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1664 struct dirent *dir_entry;
1665 boolean valid_file_found = FALSE;
1667 if ((dir = opendir(directory_path)) != NULL)
1669 while ((dir_entry = readdir(dir)) != NULL)
1671 char *entry_name = dir_entry->d_name;
1673 if (FileIsArtworkType(entry_name, type))
1675 valid_file_found = TRUE;
1683 if (!valid_file_found)
1685 if (strcmp(directory_name, ".") != 0)
1686 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1688 free(directory_path);
1695 artwork_new = newTreeInfo();
1698 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1700 setTreeInfoToDefaults(artwork_new, type);
1702 artwork_new->filename = getStringCopy(directory_name);
1704 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1707 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1710 /* set all structure fields according to the token/value pairs */
1712 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1713 setSetupInfo(levelinfo_tokens, i,
1714 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1717 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1719 free(artwork_new->name);
1720 artwork_new->name = getStringCopy(artwork_new->filename);
1724 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1727 if (artwork_new->identifier == NULL)
1728 artwork_new->identifier = getStringCopy(artwork_new->filename);
1730 if (artwork_new->name_sorting == NULL)
1731 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1734 if (node_parent == NULL) /* top level group */
1736 artwork_new->basepath = getStringCopy(base_directory);
1737 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1739 else /* sub level group */
1741 artwork_new->basepath = getStringCopy(node_parent->basepath);
1742 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1745 artwork_new->user_defined =
1746 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1748 /* (may use ".sort_priority" from "setup_file_list" above) */
1749 artwork_new->color = ARTWORKCOLOR(artwork_new);
1750 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1752 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1754 if (artwork_new->name != NULL)
1755 free(artwork_new->name);
1757 if (strcmp(artwork_new->filename, ".") == 0)
1759 if (artwork_new->user_defined)
1761 artwork_new->identifier = getStringCopy("private");
1762 artwork_new->sort_priority = ARTWORKCLASS_USER;
1766 artwork_new->identifier = getStringCopy("classic");
1767 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1770 /* set to new values after changing ".sort_priority" */
1771 artwork_new->color = ARTWORKCOLOR(artwork_new);
1772 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1776 artwork_new->identifier = getStringCopy(artwork_new->filename);
1779 artwork_new->name = getStringCopy(artwork_new->identifier);
1780 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1783 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1785 pushTreeInfo(node_first, artwork_new);
1787 freeSetupFileList(setup_file_list);
1789 free(directory_path);
1795 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1796 TreeInfo *node_parent,
1797 char *base_directory, int type)
1800 struct dirent *dir_entry;
1801 boolean valid_entry_found = FALSE;
1803 if ((dir = opendir(base_directory)) == NULL)
1805 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1806 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1810 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1812 struct stat file_status;
1813 char *directory_name = dir_entry->d_name;
1814 char *directory_path = getPath2(base_directory, directory_name);
1816 /* skip entries for current and parent directory */
1817 if (strcmp(directory_name, ".") == 0 ||
1818 strcmp(directory_name, "..") == 0)
1820 free(directory_path);
1824 /* find out if directory entry is itself a directory */
1825 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1826 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1828 free(directory_path);
1832 free(directory_path);
1834 /* check if this directory contains artwork with or without config file */
1835 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1837 directory_name, type);
1842 /* check if this directory directly contains artwork itself */
1843 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1844 base_directory, ".",
1846 if (!valid_entry_found)
1847 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1851 static TreeInfo *getDummyArtworkInfo(int type)
1853 /* this is only needed when there is completely no artwork available */
1854 TreeInfo *artwork_new = newTreeInfo();
1856 setTreeInfoToDefaults(artwork_new, type);
1858 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1859 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1860 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1862 if (artwork_new->name != NULL)
1863 free(artwork_new->name);
1865 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1866 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1867 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1872 void LoadArtworkInfo()
1874 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1876 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1877 options.graphics_directory,
1878 TREE_TYPE_GRAPHICS_DIR);
1879 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1880 getUserGraphicsDir(),
1881 TREE_TYPE_GRAPHICS_DIR);
1883 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1884 options.sounds_directory,
1885 TREE_TYPE_SOUNDS_DIR);
1886 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1888 TREE_TYPE_SOUNDS_DIR);
1890 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1891 options.music_directory,
1892 TREE_TYPE_MUSIC_DIR);
1893 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1895 TREE_TYPE_MUSIC_DIR);
1897 if (artwork.gfx_first == NULL)
1898 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1899 if (artwork.snd_first == NULL)
1900 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1901 if (artwork.mus_first == NULL)
1902 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1904 /* before sorting, the first entries will be from the user directory */
1905 artwork.gfx_current =
1906 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1907 if (artwork.gfx_current == NULL)
1908 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1910 artwork.snd_current =
1911 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1912 if (artwork.snd_current == NULL)
1913 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1915 artwork.mus_current =
1916 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1917 if (artwork.mus_current == NULL)
1918 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1920 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1921 artwork.snd_current_identifier = artwork.snd_current->identifier;
1922 artwork.mus_current_identifier = artwork.mus_current->identifier;
1925 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1926 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1927 printf("music set == %s\n\n", artwork.mus_current_identifier);
1930 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1931 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1932 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1935 dumpTreeInfo(artwork.gfx_first, 0);
1936 dumpTreeInfo(artwork.snd_first, 0);
1937 dumpTreeInfo(artwork.mus_first, 0);
1941 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1942 LevelDirTree *level_node)
1944 /* recursively check all level directories for artwork sub-directories */
1948 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1949 ARTWORK_DIRECTORY((*artwork_node)->type));
1952 if (!level_node->parent_link)
1953 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1954 level_node->filename, level_node->name);
1957 if (!level_node->parent_link)
1959 TreeInfo *topnode_last = *artwork_node;
1961 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1962 (*artwork_node)->type);
1964 if (topnode_last != *artwork_node)
1966 free((*artwork_node)->identifier);
1967 free((*artwork_node)->name);
1968 free((*artwork_node)->name_sorting);
1970 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1971 (*artwork_node)->name = getStringCopy(level_node->name);
1972 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1974 (*artwork_node)->sort_priority = level_node->sort_priority;
1975 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1981 if (level_node->node_group != NULL)
1982 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1984 level_node = level_node->next;
1988 void LoadLevelArtworkInfo()
1990 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1992 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1993 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1994 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1996 /* needed for reloading level artwork not known at ealier stage */
1997 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
1999 artwork.gfx_current =
2000 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
2001 if (artwork.gfx_current == NULL)
2002 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
2005 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
2007 artwork.snd_current =
2008 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2009 if (artwork.snd_current == NULL)
2010 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2013 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2015 artwork.mus_current =
2016 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2017 if (artwork.mus_current == NULL)
2018 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2021 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2022 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2023 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2026 dumpTreeInfo(artwork.gfx_first, 0);
2027 dumpTreeInfo(artwork.snd_first, 0);
2028 dumpTreeInfo(artwork.mus_first, 0);
2032 static void SaveUserLevelInfo()
2038 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2040 if (!(file = fopen(filename, MODE_WRITE)))
2042 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2047 /* always start with reliable default values */
2048 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2050 ldi.name = getStringCopy(getLoginName());
2051 ldi.author = getStringCopy(getRealName());
2053 ldi.first_level = 1;
2054 ldi.sort_priority = LEVELCLASS_USER_START;
2055 ldi.readonly = FALSE;
2056 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2057 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2058 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2060 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2061 getCookie("LEVELINFO")));
2063 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2064 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2065 i != LEVELINFO_TOKEN_NAME_SORTING &&
2066 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2067 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2072 SetFilePermissions(filename, PERMS_PRIVATE);
2075 char *getSetupValue(int type, void *value)
2077 static char value_string[MAX_LINE_LEN];
2085 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2089 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2093 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2097 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2101 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2105 sprintf(value_string, "%d", *(int *)value);
2109 strcpy(value_string, *(char **)value);
2113 value_string[0] = '\0';
2117 return value_string;
2120 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2124 static char token_string[MAX_LINE_LEN];
2125 int token_type = token_info[token_nr].type;
2126 void *setup_value = token_info[token_nr].value;
2127 char *token_text = token_info[token_nr].text;
2128 char *value_string = getSetupValue(token_type, setup_value);
2130 /* build complete token string */
2131 sprintf(token_string, "%s%s", prefix, token_text);
2133 /* build setup entry line */
2134 line = getFormattedSetupEntry(token_string, value_string);
2136 if (token_type == TYPE_KEY_X11)
2138 Key key = *(Key *)setup_value;
2139 char *keyname = getKeyNameFromKey(key);
2141 /* add comment, if useful */
2142 if (strcmp(keyname, "(undefined)") != 0 &&
2143 strcmp(keyname, "(unknown)") != 0)
2145 /* add at least one whitespace */
2147 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2151 strcat(line, keyname);
2158 void LoadLevelSetup_LastSeries()
2161 struct SetupFileList *level_setup_list = NULL;
2163 /* always start with reliable default values */
2164 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2166 /* ----------------------------------------------------------------------- */
2167 /* ~/.<program>/levelsetup.conf */
2168 /* ----------------------------------------------------------------------- */
2170 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2172 if ((level_setup_list = loadSetupFileList(filename)))
2174 char *last_level_series =
2175 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2177 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2179 if (leveldir_current == NULL)
2180 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2182 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2184 freeSetupFileList(level_setup_list);
2187 Error(ERR_WARN, "using default setup values");
2192 void SaveLevelSetup_LastSeries()
2195 char *level_subdir = leveldir_current->filename;
2198 /* ----------------------------------------------------------------------- */
2199 /* ~/.<program>/levelsetup.conf */
2200 /* ----------------------------------------------------------------------- */
2202 InitUserDataDirectory();
2204 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2206 if (!(file = fopen(filename, MODE_WRITE)))
2208 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2213 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2214 getCookie("LEVELSETUP")));
2215 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2221 SetFilePermissions(filename, PERMS_PRIVATE);
2224 static void checkSeriesInfo()
2226 static char *level_directory = NULL;
2228 struct dirent *dir_entry;
2230 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2232 level_directory = getPath2((leveldir_current->user_defined ?
2233 getUserLevelDir(NULL) :
2234 options.level_directory),
2235 leveldir_current->fullpath);
2237 if ((dir = opendir(level_directory)) == NULL)
2239 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2243 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2245 if (strlen(dir_entry->d_name) > 4 &&
2246 dir_entry->d_name[3] == '.' &&
2247 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2249 char levelnum_str[4];
2252 strncpy(levelnum_str, dir_entry->d_name, 3);
2253 levelnum_str[3] = '\0';
2255 levelnum_value = atoi(levelnum_str);
2257 if (levelnum_value < leveldir_current->first_level)
2259 Error(ERR_WARN, "additional level %d found", levelnum_value);
2260 leveldir_current->first_level = levelnum_value;
2262 else if (levelnum_value > leveldir_current->last_level)
2264 Error(ERR_WARN, "additional level %d found", levelnum_value);
2265 leveldir_current->last_level = levelnum_value;
2273 void LoadLevelSetup_SeriesInfo()
2276 struct SetupFileList *level_setup_list = NULL;
2277 char *level_subdir = leveldir_current->filename;
2279 /* always start with reliable default values */
2280 level_nr = leveldir_current->first_level;
2282 checkSeriesInfo(leveldir_current);
2284 /* ----------------------------------------------------------------------- */
2285 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2286 /* ----------------------------------------------------------------------- */
2288 level_subdir = leveldir_current->filename;
2290 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2292 if ((level_setup_list = loadSetupFileList(filename)))
2296 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2300 level_nr = atoi(token_value);
2302 if (level_nr < leveldir_current->first_level)
2303 level_nr = leveldir_current->first_level;
2304 if (level_nr > leveldir_current->last_level)
2305 level_nr = leveldir_current->last_level;
2308 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2312 int level_nr = atoi(token_value);
2314 if (level_nr < leveldir_current->first_level)
2315 level_nr = leveldir_current->first_level;
2316 if (level_nr > leveldir_current->last_level + 1)
2317 level_nr = leveldir_current->last_level;
2319 if (leveldir_current->user_defined)
2320 level_nr = leveldir_current->last_level;
2322 leveldir_current->handicap_level = level_nr;
2325 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2327 freeSetupFileList(level_setup_list);
2330 Error(ERR_WARN, "using default setup values");
2335 void SaveLevelSetup_SeriesInfo()
2338 char *level_subdir = leveldir_current->filename;
2339 char *level_nr_str = int2str(level_nr, 0);
2340 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2343 /* ----------------------------------------------------------------------- */
2344 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2345 /* ----------------------------------------------------------------------- */
2347 InitLevelSetupDirectory(level_subdir);
2349 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2351 if (!(file = fopen(filename, MODE_WRITE)))
2353 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2358 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2359 getCookie("LEVELSETUP")));
2360 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2362 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2363 handicap_level_str));
2368 SetFilePermissions(filename, PERMS_PRIVATE);