1 /***********************************************************
2 * Artsoft Retro-Game Library *
3 *----------------------------------------------------------*
4 * (c) 1994-2002 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include <sys/types.h>
25 /* file names and filename extensions */
26 #if !defined(PLATFORM_MSDOS)
27 #define LEVELSETUP_DIRECTORY "levelsetup"
28 #define SETUP_FILENAME "setup.conf"
29 #define LEVELSETUP_FILENAME "levelsetup.conf"
30 #define LEVELINFO_FILENAME "levelinfo.conf"
31 #define GRAPHICSINFO_FILENAME "graphicsinfo.conf"
32 #define SOUNDSINFO_FILENAME "soundsinfo.conf"
33 #define MUSICINFO_FILENAME "musicinfo.conf"
34 #define LEVELFILE_EXTENSION "level"
35 #define TAPEFILE_EXTENSION "tape"
36 #define SCOREFILE_EXTENSION "score"
38 #define LEVELSETUP_DIRECTORY "lvlsetup"
39 #define SETUP_FILENAME "setup.cnf"
40 #define LEVELSETUP_FILENAME "lvlsetup.cnf"
41 #define LEVELINFO_FILENAME "lvlinfo.cnf"
42 #define GRAPHICSINFO_FILENAME "gfxinfo.cnf"
43 #define SOUNDSINFO_FILENAME "sndinfo.cnf"
44 #define MUSICINFO_FILENAME "musinfo.cnf"
45 #define LEVELFILE_EXTENSION "lvl"
46 #define TAPEFILE_EXTENSION "tap"
47 #define SCOREFILE_EXTENSION "sco"
50 #define NUM_LEVELCLASS_DESC 8
51 static char *levelclass_desc[NUM_LEVELCLASS_DESC] =
63 #define LEVELCOLOR(n) (IS_LEVELCLASS_TUTORIAL(n) ? FC_BLUE : \
64 IS_LEVELCLASS_CLASSICS(n) ? FC_RED : \
65 IS_LEVELCLASS_BD(n) ? FC_GREEN : \
66 IS_LEVELCLASS_EM(n) ? FC_YELLOW : \
67 IS_LEVELCLASS_SP(n) ? FC_GREEN : \
68 IS_LEVELCLASS_DX(n) ? FC_YELLOW : \
69 IS_LEVELCLASS_CONTRIBUTION(n) ? FC_GREEN : \
70 IS_LEVELCLASS_USER(n) ? FC_RED : \
73 #define LEVELSORTING(n) (IS_LEVELCLASS_TUTORIAL(n) ? 0 : \
74 IS_LEVELCLASS_CLASSICS(n) ? 1 : \
75 IS_LEVELCLASS_BD(n) ? 2 : \
76 IS_LEVELCLASS_EM(n) ? 3 : \
77 IS_LEVELCLASS_SP(n) ? 4 : \
78 IS_LEVELCLASS_DX(n) ? 5 : \
79 IS_LEVELCLASS_CONTRIBUTION(n) ? 6 : \
80 IS_LEVELCLASS_USER(n) ? 7 : \
83 #define ARTWORKCOLOR(n) (IS_ARTWORKCLASS_CLASSICS(n) ? FC_RED : \
84 IS_ARTWORKCLASS_CONTRIBUTION(n) ? FC_YELLOW : \
85 IS_ARTWORKCLASS_LEVEL(n) ? FC_GREEN : \
86 IS_ARTWORKCLASS_USER(n) ? FC_RED : \
89 #define ARTWORKSORTING(n) (IS_ARTWORKCLASS_CLASSICS(n) ? 0 : \
90 IS_ARTWORKCLASS_CONTRIBUTION(n) ? 1 : \
91 IS_ARTWORKCLASS_LEVEL(n) ? 2 : \
92 IS_ARTWORKCLASS_USER(n) ? 3 : \
95 #define TOKEN_VALUE_POSITION 40
96 #define TOKEN_COMMENT_POSITION 60
98 #define MAX_COOKIE_LEN 256
100 #define ARTWORKINFO_FILENAME(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
101 GRAPHICSINFO_FILENAME : \
102 (type) == TREE_TYPE_SOUNDS_DIR ? \
103 SOUNDSINFO_FILENAME : \
104 (type) == TREE_TYPE_MUSIC_DIR ? \
105 MUSICINFO_FILENAME : "")
107 #define ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
108 GRAPHICS_DIRECTORY : \
109 (type) == TREE_TYPE_SOUNDS_DIR ? \
111 (type) == TREE_TYPE_MUSIC_DIR ? \
112 MUSIC_DIRECTORY : "")
114 #define OPTIONS_ARTWORK_DIRECTORY(type) ((type) == TREE_TYPE_GRAPHICS_DIR ? \
115 options.graphics_directory : \
116 (type) == TREE_TYPE_SOUNDS_DIR ? \
117 options.sounds_directory : \
118 (type) == TREE_TYPE_MUSIC_DIR ? \
119 options.music_directory : "")
122 /* ------------------------------------------------------------------------- */
124 /* ------------------------------------------------------------------------- */
126 static char *getLevelClassDescription(TreeInfo *ldi)
128 int position = ldi->sort_priority / 100;
130 if (position >= 0 && position < NUM_LEVELCLASS_DESC)
131 return levelclass_desc[position];
133 return "Unknown Level Class";
136 static char *getUserLevelDir(char *level_subdir)
138 static char *userlevel_dir = NULL;
139 char *data_dir = getUserDataDir();
140 char *userlevel_subdir = LEVELS_DIRECTORY;
145 if (level_subdir != NULL)
146 userlevel_dir = getPath3(data_dir, userlevel_subdir, level_subdir);
148 userlevel_dir = getPath2(data_dir, userlevel_subdir);
150 return userlevel_dir;
153 static char *getTapeDir(char *level_subdir)
155 static char *tape_dir = NULL;
156 char *data_dir = getUserDataDir();
157 char *tape_subdir = TAPES_DIRECTORY;
162 if (level_subdir != NULL)
163 tape_dir = getPath3(data_dir, tape_subdir, level_subdir);
165 tape_dir = getPath2(data_dir, tape_subdir);
170 static char *getScoreDir(char *level_subdir)
172 static char *score_dir = NULL;
173 char *data_dir = options.rw_base_directory;
174 char *score_subdir = SCORES_DIRECTORY;
179 if (level_subdir != NULL)
180 score_dir = getPath3(data_dir, score_subdir, level_subdir);
182 score_dir = getPath2(data_dir, score_subdir);
187 static char *getLevelSetupDir(char *level_subdir)
189 static char *levelsetup_dir = NULL;
190 char *data_dir = getUserDataDir();
191 char *levelsetup_subdir = LEVELSETUP_DIRECTORY;
194 free(levelsetup_dir);
196 if (level_subdir != NULL)
197 levelsetup_dir = getPath3(data_dir, levelsetup_subdir, level_subdir);
199 levelsetup_dir = getPath2(data_dir, levelsetup_subdir);
201 return levelsetup_dir;
204 static char *getLevelDirFromTreeInfo(TreeInfo *node)
206 static char *level_dir = NULL;
209 return options.level_directory;
214 level_dir = getPath2((node->user_defined ? getUserLevelDir(NULL) :
215 options.level_directory), node->fullpath);
220 static char *getCurrentLevelDir()
222 return getLevelDirFromTreeInfo(leveldir_current);
225 static char *getDefaultGraphicsDir(char *graphics_subdir)
227 static char *graphics_dir = NULL;
229 if (graphics_subdir == NULL)
230 return options.graphics_directory;
235 graphics_dir = getPath2(options.graphics_directory, graphics_subdir);
240 static char *getDefaultSoundsDir(char *sounds_subdir)
242 static char *sounds_dir = NULL;
244 if (sounds_subdir == NULL)
245 return options.sounds_directory;
250 sounds_dir = getPath2(options.sounds_directory, sounds_subdir);
255 static char *getDefaultMusicDir(char *music_subdir)
257 static char *music_dir = NULL;
259 if (music_subdir == NULL)
260 return options.music_directory;
265 music_dir = getPath2(options.music_directory, music_subdir);
270 static char *getUserGraphicsDir()
272 static char *usergraphics_dir = NULL;
274 if (usergraphics_dir == NULL)
275 usergraphics_dir = getPath2(getUserDataDir(), GRAPHICS_DIRECTORY);
277 return usergraphics_dir;
280 static char *getUserSoundsDir()
282 static char *usersounds_dir = NULL;
284 if (usersounds_dir == NULL)
285 usersounds_dir = getPath2(getUserDataDir(), SOUNDS_DIRECTORY);
287 return usersounds_dir;
290 static char *getUserMusicDir()
292 static char *usermusic_dir = NULL;
294 if (usermusic_dir == NULL)
295 usermusic_dir = getPath2(getUserDataDir(), MUSIC_DIRECTORY);
297 return usermusic_dir;
300 static char *getSetupArtworkDir(TreeInfo *ti)
302 static char *artwork_dir = NULL;
304 if (artwork_dir != NULL)
307 artwork_dir = getPath2(ti->basepath, ti->fullpath);
312 void setLevelArtworkDir(TreeInfo *ti)
314 char **artwork_path_ptr, *artwork_set;
315 TreeInfo *level_artwork;
317 if (ti == NULL || leveldir_current == NULL)
321 (ti->type == TREE_TYPE_GRAPHICS_DIR ? &leveldir_current->graphics_path :
322 ti->type == TREE_TYPE_SOUNDS_DIR ? &leveldir_current->sounds_path :
323 &leveldir_current->music_path);
326 (ti->type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_set :
327 ti->type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_set :
328 leveldir_current->music_set);
330 if ((level_artwork = getTreeInfoFromIdentifier(ti, artwork_set)) == NULL)
333 if (*artwork_path_ptr != NULL)
334 free(*artwork_path_ptr);
336 *artwork_path_ptr = getStringCopy(getSetupArtworkDir(level_artwork));
339 static char *getLevelArtworkDir(int type)
343 if (leveldir_current == NULL)
344 return UNDEFINED_FILENAME;
347 (type == TREE_TYPE_GRAPHICS_DIR ? leveldir_current->graphics_path :
348 type == TREE_TYPE_SOUNDS_DIR ? leveldir_current->sounds_path :
349 type == TREE_TYPE_MUSIC_DIR ? leveldir_current->music_path :
355 char *getLevelFilename(int nr)
357 static char *filename = NULL;
358 char basename[MAX_FILENAME_LEN];
360 if (filename != NULL)
363 sprintf(basename, "%03d.%s", nr, LEVELFILE_EXTENSION);
364 filename = getPath2(getCurrentLevelDir(), basename);
369 char *getTapeFilename(int nr)
371 static char *filename = NULL;
372 char basename[MAX_FILENAME_LEN];
374 if (filename != NULL)
377 sprintf(basename, "%03d.%s", nr, TAPEFILE_EXTENSION);
378 filename = getPath2(getTapeDir(leveldir_current->filename), basename);
383 char *getScoreFilename(int nr)
385 static char *filename = NULL;
386 char basename[MAX_FILENAME_LEN];
388 if (filename != NULL)
391 sprintf(basename, "%03d.%s", nr, SCOREFILE_EXTENSION);
392 filename = getPath2(getScoreDir(leveldir_current->filename), basename);
397 char *getSetupFilename()
399 static char *filename = NULL;
401 if (filename != NULL)
404 filename = getPath2(getSetupDir(), SETUP_FILENAME);
409 static char *getCorrectedImageBasename(char *basename)
411 char *basename_corrected = basename;
413 #if defined(PLATFORM_MSDOS)
414 if (program.filename_prefix != NULL)
416 int prefix_len = strlen(program.filename_prefix);
418 if (strncmp(basename, program.filename_prefix, prefix_len) == 0)
419 basename_corrected = &basename[prefix_len];
421 /* if corrected filename is still longer than standard MS-DOS filename
422 size (8 characters + 1 dot + 3 characters file extension), shorten
423 filename by writing file extension after 8th basename character */
424 if (strlen(basename_corrected) > 8+1+3)
426 static char *msdos_filename = NULL;
428 if (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(getScoreDir(NULL), "main score", PERMS_PUBLIC);
614 createDirectory(getScoreDir(level_subdir), "level score", PERMS_PUBLIC);
617 static void SaveUserLevelInfo();
619 void InitUserLevelDirectory(char *level_subdir)
621 if (access(getUserLevelDir(level_subdir), F_OK) != 0)
623 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
624 createDirectory(getUserLevelDir(NULL), "main user level", PERMS_PRIVATE);
625 createDirectory(getUserLevelDir(level_subdir), "user level",PERMS_PRIVATE);
631 void InitLevelSetupDirectory(char *level_subdir)
633 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
634 createDirectory(getLevelSetupDir(NULL), "main level setup", PERMS_PRIVATE);
635 createDirectory(getLevelSetupDir(level_subdir), "level setup",PERMS_PRIVATE);
639 /* ------------------------------------------------------------------------- */
640 /* some functions to handle lists of level directories */
641 /* ------------------------------------------------------------------------- */
643 TreeInfo *newTreeInfo()
645 return checked_calloc(sizeof(TreeInfo));
648 void pushTreeInfo(TreeInfo **node_first, TreeInfo *node_new)
650 node_new->next = *node_first;
651 *node_first = node_new;
654 int numTreeInfo(TreeInfo *node)
667 boolean validLevelSeries(TreeInfo *node)
669 return (node != NULL && !node->node_group && !node->parent_link);
672 TreeInfo *getFirstValidTreeInfoEntry(TreeInfo *node)
677 if (node->node_group) /* enter level group (step down into tree) */
678 return getFirstValidTreeInfoEntry(node->node_group);
679 else if (node->parent_link) /* skip start entry of level group */
681 if (node->next) /* get first real level series entry */
682 return getFirstValidTreeInfoEntry(node->next);
683 else /* leave empty level group and go on */
684 return getFirstValidTreeInfoEntry(node->node_parent->next);
686 else /* this seems to be a regular level series */
690 TreeInfo *getTreeInfoFirstGroupEntry(TreeInfo *node)
695 if (node->node_parent == NULL) /* top level group */
696 return *node->node_top;
697 else /* sub level group */
698 return node->node_parent->node_group;
701 int numTreeInfoInGroup(TreeInfo *node)
703 return numTreeInfo(getTreeInfoFirstGroupEntry(node));
706 int posTreeInfo(TreeInfo *node)
708 TreeInfo *node_cmp = getTreeInfoFirstGroupEntry(node);
713 if (node_cmp == node)
717 node_cmp = node_cmp->next;
723 TreeInfo *getTreeInfoFromPos(TreeInfo *node, int pos)
725 TreeInfo *node_default = node;
740 TreeInfo *getTreeInfoFromIdentifier(TreeInfo *node, char *identifier)
742 if (identifier == NULL)
747 if (node->node_group)
749 TreeInfo *node_group;
751 node_group = getTreeInfoFromIdentifier(node->node_group, identifier);
756 else if (!node->parent_link)
758 if (strcmp(identifier, node->identifier) == 0)
768 void dumpTreeInfo(TreeInfo *node, int depth)
772 printf("Dumping TreeInfo:\n");
776 for (i=0; i<(depth + 1) * 3; i++)
779 printf("filename == '%s' (%s) [%s] (%d)\n",
780 node->filename, node->name, node->identifier, node->sort_priority);
782 if (node->node_group != NULL)
783 dumpTreeInfo(node->node_group, depth + 1);
789 void sortTreeInfo(TreeInfo **node_first,
790 int (*compare_function)(const void *, const void *))
792 int num_nodes = numTreeInfo(*node_first);
793 TreeInfo **sort_array;
794 TreeInfo *node = *node_first;
800 /* allocate array for sorting structure pointers */
801 sort_array = checked_calloc(num_nodes * sizeof(TreeInfo *));
803 /* writing structure pointers to sorting array */
804 while (i < num_nodes && node) /* double boundary check... */
806 sort_array[i] = node;
812 /* sorting the structure pointers in the sorting array */
813 qsort(sort_array, num_nodes, sizeof(TreeInfo *),
816 /* update the linkage of list elements with the sorted node array */
817 for (i=0; i<num_nodes - 1; i++)
818 sort_array[i]->next = sort_array[i + 1];
819 sort_array[num_nodes - 1]->next = NULL;
821 /* update the linkage of the main list anchor pointer */
822 *node_first = sort_array[0];
826 /* now recursively sort the level group structures */
830 if (node->node_group != NULL)
831 sortTreeInfo(&node->node_group, compare_function);
838 /* ========================================================================= */
839 /* some stuff from "files.c" */
840 /* ========================================================================= */
842 #if defined(PLATFORM_WIN32)
844 #define S_IRGRP S_IRUSR
847 #define S_IROTH S_IRUSR
850 #define S_IWGRP S_IWUSR
853 #define S_IWOTH S_IWUSR
856 #define S_IXGRP S_IXUSR
859 #define S_IXOTH S_IXUSR
862 #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP)
867 #endif /* PLATFORM_WIN32 */
869 /* file permissions for newly written files */
870 #define MODE_R_ALL (S_IRUSR | S_IRGRP | S_IROTH)
871 #define MODE_W_ALL (S_IWUSR | S_IWGRP | S_IWOTH)
872 #define MODE_X_ALL (S_IXUSR | S_IXGRP | S_IXOTH)
874 #define MODE_W_PRIVATE (S_IWUSR)
875 #define MODE_W_PUBLIC (S_IWUSR | S_IWGRP)
876 #define MODE_W_PUBLIC_DIR (S_IWUSR | S_IWGRP | S_ISGID)
878 #define DIR_PERMS_PRIVATE (MODE_R_ALL | MODE_X_ALL | MODE_W_PRIVATE)
879 #define DIR_PERMS_PUBLIC (MODE_R_ALL | MODE_X_ALL | MODE_W_PUBLIC_DIR)
881 #define FILE_PERMS_PRIVATE (MODE_R_ALL | MODE_W_PRIVATE)
882 #define FILE_PERMS_PUBLIC (MODE_R_ALL | MODE_W_PUBLIC)
884 char *getUserDataDir(void)
886 static char *userdata_dir = NULL;
890 char *home_dir = getHomeDir();
891 char *data_dir = program.userdata_directory;
893 userdata_dir = getPath2(home_dir, data_dir);
901 return getUserDataDir();
904 static mode_t posix_umask(mode_t mask)
906 #if defined(PLATFORM_UNIX)
913 static int posix_mkdir(const char *pathname, mode_t mode)
915 #if defined(PLATFORM_WIN32)
916 return mkdir(pathname);
918 return mkdir(pathname, mode);
922 void createDirectory(char *dir, char *text, int permission_class)
924 /* leave "other" permissions in umask untouched, but ensure group parts
925 of USERDATA_DIR_MODE are not masked */
926 mode_t dir_mode = (permission_class == PERMS_PRIVATE ?
927 DIR_PERMS_PRIVATE : DIR_PERMS_PUBLIC);
928 mode_t normal_umask = posix_umask(0);
929 mode_t group_umask = ~(dir_mode & S_IRWXG);
930 posix_umask(normal_umask & group_umask);
932 if (access(dir, F_OK) != 0)
933 if (posix_mkdir(dir, dir_mode) != 0)
934 Error(ERR_WARN, "cannot create %s directory '%s'", text, dir);
936 posix_umask(normal_umask); /* reset normal umask */
939 void InitUserDataDirectory()
941 createDirectory(getUserDataDir(), "user data", PERMS_PRIVATE);
944 void SetFilePermissions(char *filename, int permission_class)
946 chmod(filename, (permission_class == PERMS_PRIVATE ?
947 FILE_PERMS_PRIVATE : FILE_PERMS_PUBLIC));
950 char *getCookie(char *file_type)
952 static char cookie[MAX_COOKIE_LEN + 1];
954 if (strlen(program.cookie_prefix) + 1 +
955 strlen(file_type) + strlen("_FILE_VERSION_x.x") > MAX_COOKIE_LEN)
956 return "[COOKIE ERROR]"; /* should never happen */
958 sprintf(cookie, "%s_%s_FILE_VERSION_%d.%d",
959 program.cookie_prefix, file_type,
960 program.version_major, program.version_minor);
965 int getFileVersionFromCookieString(const char *cookie)
967 const char *ptr_cookie1, *ptr_cookie2;
968 const char *pattern1 = "_FILE_VERSION_";
969 const char *pattern2 = "?.?";
970 const int len_cookie = strlen(cookie);
971 const int len_pattern1 = strlen(pattern1);
972 const int len_pattern2 = strlen(pattern2);
973 const int len_pattern = len_pattern1 + len_pattern2;
974 int version_major, version_minor;
976 if (len_cookie <= len_pattern)
979 ptr_cookie1 = &cookie[len_cookie - len_pattern];
980 ptr_cookie2 = &cookie[len_cookie - len_pattern2];
982 if (strncmp(ptr_cookie1, pattern1, len_pattern1) != 0)
985 if (ptr_cookie2[0] < '0' || ptr_cookie2[0] > '9' ||
986 ptr_cookie2[1] != '.' ||
987 ptr_cookie2[2] < '0' || ptr_cookie2[2] > '9')
990 version_major = ptr_cookie2[0] - '0';
991 version_minor = ptr_cookie2[2] - '0';
993 return VERSION_IDENT(version_major, version_minor, 0);
996 boolean checkCookieString(const char *cookie, const char *template)
998 const char *pattern = "_FILE_VERSION_?.?";
999 const int len_cookie = strlen(cookie);
1000 const int len_template = strlen(template);
1001 const int len_pattern = strlen(pattern);
1003 if (len_cookie != len_template)
1006 if (strncmp(cookie, template, len_cookie - len_pattern) != 0)
1012 /* ------------------------------------------------------------------------- */
1013 /* setup file list handling functions */
1014 /* ------------------------------------------------------------------------- */
1016 char *getFormattedSetupEntry(char *token, char *value)
1019 static char entry[MAX_LINE_LEN];
1021 /* start with the token and some spaces to format output line */
1022 sprintf(entry, "%s:", token);
1023 for (i=strlen(entry); i<TOKEN_VALUE_POSITION; i++)
1026 /* continue with the token's value */
1027 strcat(entry, value);
1032 void freeSetupFileList(struct SetupFileList *setup_file_list)
1034 if (setup_file_list == NULL)
1037 if (setup_file_list->token)
1038 free(setup_file_list->token);
1039 if (setup_file_list->value)
1040 free(setup_file_list->value);
1041 if (setup_file_list->next)
1042 freeSetupFileList(setup_file_list->next);
1043 free(setup_file_list);
1046 struct SetupFileList *newSetupFileList(char *token, char *value)
1048 struct SetupFileList *new = checked_malloc(sizeof(struct SetupFileList));
1050 new->token = getStringCopy(token);
1051 new->value = getStringCopy(value);
1058 char *getTokenValue(struct SetupFileList *setup_file_list, char *token)
1060 if (setup_file_list == NULL)
1063 if (strcmp(setup_file_list->token, token) == 0)
1064 return setup_file_list->value;
1066 return getTokenValue(setup_file_list->next, token);
1069 void setTokenValue(struct SetupFileList *setup_file_list,
1070 char *token, char *value)
1072 if (setup_file_list == NULL)
1075 if (strcmp(setup_file_list->token, token) == 0)
1077 if (setup_file_list->value)
1078 free(setup_file_list->value);
1080 setup_file_list->value = getStringCopy(value);
1082 else if (setup_file_list->next == NULL)
1083 setup_file_list->next = newSetupFileList(token, value);
1085 setTokenValue(setup_file_list->next, token, value);
1089 static void printSetupFileList(struct SetupFileList *setup_file_list)
1091 if (!setup_file_list)
1094 printf("token: '%s'\n", setup_file_list->token);
1095 printf("value: '%s'\n", setup_file_list->value);
1097 printSetupFileList(setup_file_list->next);
1101 struct SetupFileList *loadSetupFileList(char *filename)
1104 char line[MAX_LINE_LEN];
1105 char *token, *value, *line_ptr;
1106 struct SetupFileList *setup_file_list = newSetupFileList("", "");
1107 struct SetupFileList *first_valid_list_entry;
1111 if (!(file = fopen(filename, MODE_READ)))
1113 Error(ERR_WARN, "cannot open configuration file '%s'", filename);
1119 /* read next line of input file */
1120 if (!fgets(line, MAX_LINE_LEN, file))
1123 /* cut trailing comment or whitespace from input line */
1124 for (line_ptr = line; *line_ptr; line_ptr++)
1126 if (*line_ptr == '#' || *line_ptr == '\n' || *line_ptr == '\r')
1133 /* cut trailing whitespaces from input line */
1134 for (line_ptr = &line[strlen(line)]; line_ptr > line; line_ptr--)
1135 if ((*line_ptr == ' ' || *line_ptr == '\t') && line_ptr[1] == '\0')
1138 /* ignore empty lines */
1142 line_len = strlen(line);
1144 /* cut leading whitespaces from token */
1145 for (token = line; *token; token++)
1146 if (*token != ' ' && *token != '\t')
1149 /* find end of token */
1150 for (line_ptr = token; *line_ptr; line_ptr++)
1152 if (*line_ptr == ' ' || *line_ptr == '\t' || *line_ptr == ':')
1159 if (line_ptr < line + line_len)
1160 value = line_ptr + 1;
1164 /* cut leading whitespaces from value */
1165 for (; *value; value++)
1166 if (*value != ' ' && *value != '\t')
1169 if (*token && *value)
1170 setTokenValue(setup_file_list, token, value);
1175 first_valid_list_entry = setup_file_list->next;
1177 /* free empty list header */
1178 setup_file_list->next = NULL;
1179 freeSetupFileList(setup_file_list);
1181 if (first_valid_list_entry == NULL)
1182 Error(ERR_WARN, "configuration file '%s' is empty", filename);
1184 return first_valid_list_entry;
1187 void checkSetupFileListIdentifier(struct SetupFileList *setup_file_list,
1190 if (!setup_file_list)
1193 if (strcmp(setup_file_list->token, TOKEN_STR_FILE_IDENTIFIER) == 0)
1195 if (!checkCookieString(setup_file_list->value, identifier))
1197 Error(ERR_WARN, "configuration file has wrong file identifier");
1204 if (setup_file_list->next)
1205 checkSetupFileListIdentifier(setup_file_list->next, identifier);
1208 Error(ERR_WARN, "configuration file has no file identifier");
1214 /* ========================================================================= */
1215 /* setup file stuff */
1216 /* ========================================================================= */
1218 #define TOKEN_STR_LAST_LEVEL_SERIES "last_level_series"
1219 #define TOKEN_STR_LAST_PLAYED_LEVEL "last_played_level"
1220 #define TOKEN_STR_HANDICAP_LEVEL "handicap_level"
1222 /* level directory info */
1223 #define LEVELINFO_TOKEN_IDENTIFIER 0
1224 #define LEVELINFO_TOKEN_NAME 1
1225 #define LEVELINFO_TOKEN_NAME_SORTING 2
1226 #define LEVELINFO_TOKEN_AUTHOR 3
1227 #define LEVELINFO_TOKEN_IMPORTED_FROM 4
1228 #define LEVELINFO_TOKEN_LEVELS 5
1229 #define LEVELINFO_TOKEN_FIRST_LEVEL 6
1230 #define LEVELINFO_TOKEN_SORT_PRIORITY 7
1231 #define LEVELINFO_TOKEN_LEVEL_GROUP 8
1232 #define LEVELINFO_TOKEN_READONLY 9
1233 #define LEVELINFO_TOKEN_GRAPHICS_SET 10
1234 #define LEVELINFO_TOKEN_SOUNDS_SET 11
1235 #define LEVELINFO_TOKEN_MUSIC_SET 12
1237 #define NUM_LEVELINFO_TOKENS 13
1239 static LevelDirTree ldi;
1241 static struct TokenInfo levelinfo_tokens[] =
1243 /* level directory info */
1244 { TYPE_STRING, &ldi.identifier, "identifier" },
1245 { TYPE_STRING, &ldi.name, "name" },
1246 { TYPE_STRING, &ldi.name_sorting, "name_sorting" },
1247 { TYPE_STRING, &ldi.author, "author" },
1248 { TYPE_STRING, &ldi.imported_from, "imported_from" },
1249 { TYPE_INTEGER, &ldi.levels, "levels" },
1250 { TYPE_INTEGER, &ldi.first_level, "first_level" },
1251 { TYPE_INTEGER, &ldi.sort_priority, "sort_priority" },
1252 { TYPE_BOOLEAN, &ldi.level_group, "level_group" },
1253 { TYPE_BOOLEAN, &ldi.readonly, "readonly" },
1254 { TYPE_STRING, &ldi.graphics_set, "graphics_set" },
1255 { TYPE_STRING, &ldi.sounds_set, "sounds_set" },
1256 { TYPE_STRING, &ldi.music_set, "music_set" }
1259 static void setTreeInfoToDefaults(TreeInfo *ldi, int type)
1263 ldi->node_top = (ldi->type == TREE_TYPE_LEVEL_DIR ? &leveldir_first :
1264 ldi->type == TREE_TYPE_GRAPHICS_DIR ? &artwork.gfx_first :
1265 ldi->type == TREE_TYPE_SOUNDS_DIR ? &artwork.snd_first :
1266 ldi->type == TREE_TYPE_MUSIC_DIR ? &artwork.mus_first :
1269 ldi->node_parent = NULL;
1270 ldi->node_group = NULL;
1274 ldi->cl_cursor = -1;
1276 ldi->filename = NULL;
1277 ldi->fullpath = NULL;
1278 ldi->basepath = NULL;
1279 ldi->identifier = NULL;
1280 ldi->name = getStringCopy(ANONYMOUS_NAME);
1281 ldi->name_sorting = NULL;
1282 ldi->author = getStringCopy(ANONYMOUS_NAME);
1284 ldi->sort_priority = LEVELCLASS_UNDEFINED; /* default: least priority */
1285 ldi->parent_link = FALSE;
1286 ldi->user_defined = FALSE;
1288 ldi->class_desc = NULL;
1290 if (ldi->type == TREE_TYPE_LEVEL_DIR)
1292 ldi->imported_from = NULL;
1293 ldi->graphics_set = NULL;
1294 ldi->sounds_set = NULL;
1295 ldi->music_set = NULL;
1296 ldi->graphics_path = getStringCopy(UNDEFINED_FILENAME);
1297 ldi->sounds_path = getStringCopy(UNDEFINED_FILENAME);
1298 ldi->music_path = getStringCopy(UNDEFINED_FILENAME);
1300 ldi->first_level = 0;
1301 ldi->last_level = 0;
1302 ldi->level_group = FALSE;
1303 ldi->handicap_level = 0;
1304 ldi->readonly = TRUE;
1308 static void setTreeInfoToDefaultsFromParent(TreeInfo *ldi, TreeInfo *parent)
1312 Error(ERR_WARN, "setTreeInfoToDefaultsFromParent(): parent == NULL");
1314 setTreeInfoToDefaults(ldi, TREE_TYPE_GENERIC);
1318 /* first copy all values from the parent structure ... */
1321 /* ... then set all fields to default that cannot be inherited from parent.
1322 This is especially important for all those fields that can be set from
1323 the 'levelinfo.conf' config file, because the function 'setSetupInfo()'
1324 calls 'free()' for all already set token values which requires that no
1325 other structure's pointer may point to them!
1328 ldi->filename = NULL;
1329 ldi->fullpath = NULL;
1330 ldi->basepath = NULL;
1331 ldi->identifier = NULL;
1332 ldi->name = getStringCopy(ANONYMOUS_NAME);
1333 ldi->name_sorting = NULL;
1334 ldi->author = getStringCopy(parent->author);
1335 ldi->imported_from = getStringCopy(parent->imported_from);
1337 ldi->level_group = FALSE;
1338 ldi->parent_link = FALSE;
1340 ldi->node_top = parent->node_top;
1341 ldi->node_parent = parent;
1342 ldi->node_group = NULL;
1346 void setSetupInfo(struct TokenInfo *token_info,
1347 int token_nr, char *token_value)
1349 int token_type = token_info[token_nr].type;
1350 void *setup_value = token_info[token_nr].value;
1352 if (token_value == NULL)
1355 /* set setup field to corresponding token value */
1360 *(boolean *)setup_value = get_boolean_from_string(token_value);
1364 *(Key *)setup_value = getKeyFromKeyName(token_value);
1368 *(Key *)setup_value = getKeyFromX11KeyName(token_value);
1372 *(int *)setup_value = get_integer_from_string(token_value);
1376 if (*(char **)setup_value != NULL)
1377 free(*(char **)setup_value);
1378 *(char **)setup_value = getStringCopy(token_value);
1386 static int compareTreeInfoEntries(const void *object1, const void *object2)
1388 const TreeInfo *entry1 = *((TreeInfo **)object1);
1389 const TreeInfo *entry2 = *((TreeInfo **)object2);
1390 int class_sorting1, class_sorting2;
1393 if (entry1->type == TREE_TYPE_LEVEL_DIR)
1395 class_sorting1 = LEVELSORTING(entry1);
1396 class_sorting2 = LEVELSORTING(entry2);
1400 class_sorting1 = ARTWORKSORTING(entry1);
1401 class_sorting2 = ARTWORKSORTING(entry2);
1404 if (entry1->parent_link || entry2->parent_link)
1405 compare_result = (entry1->parent_link ? -1 : +1);
1406 else if (entry1->sort_priority == entry2->sort_priority)
1408 char *name1 = getStringToLower(entry1->name_sorting);
1409 char *name2 = getStringToLower(entry2->name_sorting);
1411 compare_result = strcmp(name1, name2);
1416 else if (class_sorting1 == class_sorting2)
1417 compare_result = entry1->sort_priority - entry2->sort_priority;
1419 compare_result = class_sorting1 - class_sorting2;
1421 return compare_result;
1424 static void createParentTreeInfoNode(TreeInfo *node_parent)
1428 if (node_parent == NULL)
1431 ti_new = newTreeInfo();
1432 setTreeInfoToDefaults(ti_new, node_parent->type);
1434 ti_new->node_parent = node_parent;
1435 ti_new->parent_link = TRUE;
1437 ti_new->identifier = getStringCopy(node_parent->identifier);
1438 ti_new->name = ".. (parent directory)";
1439 ti_new->name_sorting = getStringCopy(ti_new->name);
1441 ti_new->filename = "..";
1442 ti_new->fullpath = getStringCopy(node_parent->fullpath);
1444 ti_new->sort_priority = node_parent->sort_priority;
1445 ti_new->class_desc = getLevelClassDescription(ti_new);
1447 pushTreeInfo(&node_parent->node_group, ti_new);
1450 /* forward declaration for recursive call by "LoadLevelInfoFromLevelDir()" */
1451 static void LoadLevelInfoFromLevelDir(TreeInfo **, TreeInfo *, char *);
1453 static boolean LoadLevelInfoFromLevelConf(TreeInfo **node_first,
1454 TreeInfo *node_parent,
1455 char *level_directory,
1456 char *directory_name)
1458 char *directory_path = getPath2(level_directory, directory_name);
1459 char *filename = getPath2(directory_path, LEVELINFO_FILENAME);
1460 struct SetupFileList *setup_file_list = loadSetupFileList(filename);
1461 LevelDirTree *leveldir_new = NULL;
1464 if (setup_file_list == NULL)
1466 Error(ERR_WARN, "ignoring level directory '%s'", directory_path);
1468 free(directory_path);
1474 leveldir_new = newTreeInfo();
1477 setTreeInfoToDefaultsFromParent(leveldir_new, node_parent);
1479 setTreeInfoToDefaults(leveldir_new, TREE_TYPE_LEVEL_DIR);
1481 leveldir_new->filename = getStringCopy(directory_name);
1483 checkSetupFileListIdentifier(setup_file_list, getCookie("LEVELINFO"));
1485 /* set all structure fields according to the token/value pairs */
1486 ldi = *leveldir_new;
1487 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1488 setSetupInfo(levelinfo_tokens, i,
1489 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1490 *leveldir_new = ldi;
1492 if (strcmp(leveldir_new->name, ANONYMOUS_NAME) == 0)
1494 free(leveldir_new->name);
1495 leveldir_new->name = getStringCopy(leveldir_new->filename);
1498 DrawInitText(leveldir_new->name, 150, FC_YELLOW);
1500 if (leveldir_new->identifier == NULL)
1501 leveldir_new->identifier = getStringCopy(leveldir_new->filename);
1503 if (leveldir_new->name_sorting == NULL)
1504 leveldir_new->name_sorting = getStringCopy(leveldir_new->name);
1506 if (node_parent == NULL) /* top level group */
1508 leveldir_new->basepath = level_directory;
1509 leveldir_new->fullpath = leveldir_new->filename;
1511 else /* sub level group */
1513 leveldir_new->basepath = node_parent->basepath;
1514 leveldir_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1517 if (leveldir_new->levels < 1)
1518 leveldir_new->levels = 1;
1520 leveldir_new->last_level =
1521 leveldir_new->first_level + leveldir_new->levels - 1;
1523 leveldir_new->user_defined =
1524 (leveldir_new->basepath == options.level_directory ? FALSE : TRUE);
1526 leveldir_new->color = LEVELCOLOR(leveldir_new);
1527 leveldir_new->class_desc = getLevelClassDescription(leveldir_new);
1529 leveldir_new->handicap_level = /* set handicap to default value */
1530 (leveldir_new->user_defined ?
1531 leveldir_new->last_level :
1532 leveldir_new->first_level);
1534 pushTreeInfo(node_first, leveldir_new);
1536 freeSetupFileList(setup_file_list);
1538 if (leveldir_new->level_group)
1540 /* create node to link back to current level directory */
1541 createParentTreeInfoNode(leveldir_new);
1543 /* step into sub-directory and look for more level series */
1544 LoadLevelInfoFromLevelDir(&leveldir_new->node_group,
1545 leveldir_new, directory_path);
1548 free(directory_path);
1554 static void LoadLevelInfoFromLevelDir(TreeInfo **node_first,
1555 TreeInfo *node_parent,
1556 char *level_directory)
1559 struct dirent *dir_entry;
1560 boolean valid_entry_found = FALSE;
1562 if ((dir = opendir(level_directory)) == NULL)
1564 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
1568 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1570 struct stat file_status;
1571 char *directory_name = dir_entry->d_name;
1572 char *directory_path = getPath2(level_directory, directory_name);
1574 /* skip entries for current and parent directory */
1575 if (strcmp(directory_name, ".") == 0 ||
1576 strcmp(directory_name, "..") == 0)
1578 free(directory_path);
1582 /* find out if directory entry is itself a directory */
1583 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1584 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1586 free(directory_path);
1590 free(directory_path);
1592 if (strcmp(directory_name, GRAPHICS_DIRECTORY) == 0 ||
1593 strcmp(directory_name, SOUNDS_DIRECTORY) == 0 ||
1594 strcmp(directory_name, MUSIC_DIRECTORY) == 0)
1597 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1604 if (!valid_entry_found)
1606 /* check if this directory directly contains a file "levelinfo.conf" */
1607 valid_entry_found |= LoadLevelInfoFromLevelConf(node_first, node_parent,
1608 level_directory, ".");
1611 if (!valid_entry_found)
1612 Error(ERR_WARN, "cannot find any valid level series in directory '%s'",
1616 void LoadLevelInfo()
1618 InitUserLevelDirectory(getLoginName());
1620 DrawInitText("Loading level series:", 120, FC_GREEN);
1622 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, options.level_directory);
1623 LoadLevelInfoFromLevelDir(&leveldir_first, NULL, getUserLevelDir(NULL));
1625 /* before sorting, the first entries will be from the user directory */
1626 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
1628 if (leveldir_first == NULL)
1629 Error(ERR_EXIT, "cannot find any valid level series in any directory");
1631 sortTreeInfo(&leveldir_first, compareTreeInfoEntries);
1634 dumpTreeInfo(leveldir_first, 0);
1638 static boolean LoadArtworkInfoFromArtworkConf(TreeInfo **node_first,
1639 TreeInfo *node_parent,
1640 char *base_directory,
1641 char *directory_name, int type)
1643 char *directory_path = getPath2(base_directory, directory_name);
1644 char *filename = getPath2(directory_path, ARTWORKINFO_FILENAME(type));
1645 struct SetupFileList *setup_file_list = NULL;
1646 TreeInfo *artwork_new = NULL;
1649 if (access(filename, F_OK) == 0) /* file exists */
1650 setup_file_list = loadSetupFileList(filename);
1652 if (setup_file_list == NULL) /* no config file -- look for artwork files */
1655 struct dirent *dir_entry;
1656 boolean valid_file_found = FALSE;
1658 if ((dir = opendir(directory_path)) != NULL)
1660 while ((dir_entry = readdir(dir)) != NULL)
1662 char *entry_name = dir_entry->d_name;
1664 if (FileIsArtworkType(entry_name, type))
1666 valid_file_found = TRUE;
1674 if (!valid_file_found)
1676 if (strcmp(directory_name, ".") != 0)
1677 Error(ERR_WARN, "ignoring artwork directory '%s'", directory_path);
1679 free(directory_path);
1686 artwork_new = newTreeInfo();
1689 setTreeInfoToDefaultsFromParent(artwork_new, node_parent);
1691 setTreeInfoToDefaults(artwork_new, type);
1693 artwork_new->filename = getStringCopy(directory_name);
1695 if (setup_file_list) /* (before defining ".color" and ".class_desc") */
1698 checkSetupFileListIdentifier(setup_file_list, getCookie("..."));
1701 /* set all structure fields according to the token/value pairs */
1703 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
1704 setSetupInfo(levelinfo_tokens, i,
1705 getTokenValue(setup_file_list, levelinfo_tokens[i].text));
1708 if (strcmp(artwork_new->name, ANONYMOUS_NAME) == 0)
1710 free(artwork_new->name);
1711 artwork_new->name = getStringCopy(artwork_new->filename);
1715 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1718 if (artwork_new->identifier == NULL)
1719 artwork_new->identifier = getStringCopy(artwork_new->filename);
1721 if (artwork_new->name_sorting == NULL)
1722 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1725 if (node_parent == NULL) /* top level group */
1727 artwork_new->basepath = getStringCopy(base_directory);
1728 artwork_new->fullpath = getStringCopy(artwork_new->filename);
1730 else /* sub level group */
1732 artwork_new->basepath = getStringCopy(node_parent->basepath);
1733 artwork_new->fullpath = getPath2(node_parent->fullpath, directory_name);
1736 artwork_new->user_defined =
1737 (artwork_new->basepath == OPTIONS_ARTWORK_DIRECTORY(type) ? FALSE : TRUE);
1739 /* (may use ".sort_priority" from "setup_file_list" above) */
1740 artwork_new->color = ARTWORKCOLOR(artwork_new);
1741 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1743 if (setup_file_list == NULL) /* (after determining ".user_defined") */
1745 if (artwork_new->name != NULL)
1746 free(artwork_new->name);
1748 if (strcmp(artwork_new->filename, ".") == 0)
1750 if (artwork_new->user_defined)
1752 artwork_new->identifier = getStringCopy("private");
1753 artwork_new->sort_priority = ARTWORKCLASS_USER;
1757 artwork_new->identifier = getStringCopy("classic");
1758 artwork_new->sort_priority = ARTWORKCLASS_CLASSICS;
1761 /* set to new values after changing ".sort_priority" */
1762 artwork_new->color = ARTWORKCOLOR(artwork_new);
1763 artwork_new->class_desc = getLevelClassDescription(artwork_new);
1767 artwork_new->identifier = getStringCopy(artwork_new->filename);
1770 artwork_new->name = getStringCopy(artwork_new->identifier);
1771 artwork_new->name_sorting = getStringCopy(artwork_new->name);
1774 DrawInitText(artwork_new->name, 150, FC_YELLOW);
1776 pushTreeInfo(node_first, artwork_new);
1778 freeSetupFileList(setup_file_list);
1780 free(directory_path);
1786 static void LoadArtworkInfoFromArtworkDir(TreeInfo **node_first,
1787 TreeInfo *node_parent,
1788 char *base_directory, int type)
1791 struct dirent *dir_entry;
1792 boolean valid_entry_found = FALSE;
1794 if ((dir = opendir(base_directory)) == NULL)
1796 if (base_directory == OPTIONS_ARTWORK_DIRECTORY(type))
1797 Error(ERR_WARN, "cannot read directory '%s'", base_directory);
1801 while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
1803 struct stat file_status;
1804 char *directory_name = dir_entry->d_name;
1805 char *directory_path = getPath2(base_directory, directory_name);
1807 /* skip entries for current and parent directory */
1808 if (strcmp(directory_name, ".") == 0 ||
1809 strcmp(directory_name, "..") == 0)
1811 free(directory_path);
1815 /* find out if directory entry is itself a directory */
1816 if (stat(directory_path, &file_status) != 0 || /* cannot stat file */
1817 (file_status.st_mode & S_IFMT) != S_IFDIR) /* not a directory */
1819 free(directory_path);
1823 free(directory_path);
1825 /* check if this directory contains artwork with or without config file */
1826 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1828 directory_name, type);
1833 /* check if this directory directly contains artwork itself */
1834 valid_entry_found |= LoadArtworkInfoFromArtworkConf(node_first,node_parent,
1835 base_directory, ".",
1837 if (!valid_entry_found)
1838 Error(ERR_WARN, "cannot find any valid artwork in directory '%s'",
1842 static TreeInfo *getDummyArtworkInfo(int type)
1844 /* this is only needed when there is completely no artwork available */
1845 TreeInfo *artwork_new = newTreeInfo();
1847 setTreeInfoToDefaults(artwork_new, type);
1849 artwork_new->filename = getStringCopy(UNDEFINED_FILENAME);
1850 artwork_new->fullpath = getStringCopy(UNDEFINED_FILENAME);
1851 artwork_new->basepath = getStringCopy(UNDEFINED_FILENAME);
1853 if (artwork_new->name != NULL)
1854 free(artwork_new->name);
1856 artwork_new->identifier = getStringCopy(UNDEFINED_FILENAME);
1857 artwork_new->name = getStringCopy(UNDEFINED_FILENAME);
1858 artwork_new->name_sorting = getStringCopy(UNDEFINED_FILENAME);
1863 void LoadArtworkInfo()
1865 DrawInitText("Looking for custom artwork:", 120, FC_GREEN);
1867 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1868 options.graphics_directory,
1869 TREE_TYPE_GRAPHICS_DIR);
1870 LoadArtworkInfoFromArtworkDir(&artwork.gfx_first, NULL,
1871 getUserGraphicsDir(),
1872 TREE_TYPE_GRAPHICS_DIR);
1874 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1875 options.sounds_directory,
1876 TREE_TYPE_SOUNDS_DIR);
1877 LoadArtworkInfoFromArtworkDir(&artwork.snd_first, NULL,
1879 TREE_TYPE_SOUNDS_DIR);
1881 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1882 options.music_directory,
1883 TREE_TYPE_MUSIC_DIR);
1884 LoadArtworkInfoFromArtworkDir(&artwork.mus_first, NULL,
1886 TREE_TYPE_MUSIC_DIR);
1888 if (artwork.gfx_first == NULL)
1889 artwork.gfx_first = getDummyArtworkInfo(TREE_TYPE_GRAPHICS_DIR);
1890 if (artwork.snd_first == NULL)
1891 artwork.snd_first = getDummyArtworkInfo(TREE_TYPE_SOUNDS_DIR);
1892 if (artwork.mus_first == NULL)
1893 artwork.mus_first = getDummyArtworkInfo(TREE_TYPE_MUSIC_DIR);
1895 /* before sorting, the first entries will be from the user directory */
1896 artwork.gfx_current =
1897 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1898 if (artwork.gfx_current == NULL)
1899 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1901 artwork.snd_current =
1902 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
1903 if (artwork.snd_current == NULL)
1904 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
1906 artwork.mus_current =
1907 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
1908 if (artwork.mus_current == NULL)
1909 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
1911 artwork.gfx_current_identifier = artwork.gfx_current->identifier;
1912 artwork.snd_current_identifier = artwork.snd_current->identifier;
1913 artwork.mus_current_identifier = artwork.mus_current->identifier;
1916 printf("graphics set == %s\n\n", artwork.gfx_current_identifier);
1917 printf("sounds set == %s\n\n", artwork.snd_current_identifier);
1918 printf("music set == %s\n\n", artwork.mus_current_identifier);
1921 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
1922 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
1923 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
1926 dumpTreeInfo(artwork.gfx_first, 0);
1927 dumpTreeInfo(artwork.snd_first, 0);
1928 dumpTreeInfo(artwork.mus_first, 0);
1932 void LoadArtworkInfoFromLevelInfo(ArtworkDirTree **artwork_node,
1933 LevelDirTree *level_node)
1935 /* recursively check all level directories for artwork sub-directories */
1939 char *path = getPath2(getLevelDirFromTreeInfo(level_node),
1940 ARTWORK_DIRECTORY((*artwork_node)->type));
1943 if (!level_node->parent_link)
1944 printf("CHECKING '%s' ['%s', '%s'] ...\n", path,
1945 level_node->filename, level_node->name);
1948 if (!level_node->parent_link)
1950 TreeInfo *topnode_last = *artwork_node;
1952 LoadArtworkInfoFromArtworkDir(artwork_node, NULL, path,
1953 (*artwork_node)->type);
1955 if (topnode_last != *artwork_node)
1957 free((*artwork_node)->identifier);
1958 free((*artwork_node)->name);
1959 free((*artwork_node)->name_sorting);
1961 (*artwork_node)->identifier = getStringCopy(level_node->filename);
1962 (*artwork_node)->name = getStringCopy(level_node->name);
1963 (*artwork_node)->name_sorting = getStringCopy(level_node->name);
1965 (*artwork_node)->sort_priority = level_node->sort_priority;
1966 (*artwork_node)->color = LEVELCOLOR((*artwork_node));
1972 if (level_node->node_group != NULL)
1973 LoadArtworkInfoFromLevelInfo(artwork_node, level_node->node_group);
1975 level_node = level_node->next;
1979 void LoadLevelArtworkInfo()
1981 DrawInitText("Looking for custom level artwork:", 120, FC_GREEN);
1983 LoadArtworkInfoFromLevelInfo(&artwork.gfx_first, leveldir_first);
1984 LoadArtworkInfoFromLevelInfo(&artwork.snd_first, leveldir_first);
1985 LoadArtworkInfoFromLevelInfo(&artwork.mus_first, leveldir_first);
1987 /* needed for reloading level artwork not known at ealier stage */
1988 if (strcmp(artwork.gfx_current_identifier, setup.graphics_set) != 0)
1990 artwork.gfx_current =
1991 getTreeInfoFromIdentifier(artwork.gfx_first, setup.graphics_set);
1992 if (artwork.gfx_current == NULL)
1993 artwork.gfx_current = getFirstValidTreeInfoEntry(artwork.gfx_first);
1996 if (strcmp(artwork.snd_current_identifier, setup.sounds_set) != 0)
1998 artwork.snd_current =
1999 getTreeInfoFromIdentifier(artwork.snd_first, setup.sounds_set);
2000 if (artwork.snd_current == NULL)
2001 artwork.snd_current = getFirstValidTreeInfoEntry(artwork.snd_first);
2004 if (strcmp(artwork.mus_current_identifier, setup.music_set) != 0)
2006 artwork.mus_current =
2007 getTreeInfoFromIdentifier(artwork.mus_first, setup.music_set);
2008 if (artwork.mus_current == NULL)
2009 artwork.mus_current = getFirstValidTreeInfoEntry(artwork.mus_first);
2012 sortTreeInfo(&artwork.gfx_first, compareTreeInfoEntries);
2013 sortTreeInfo(&artwork.snd_first, compareTreeInfoEntries);
2014 sortTreeInfo(&artwork.mus_first, compareTreeInfoEntries);
2017 dumpTreeInfo(artwork.gfx_first, 0);
2018 dumpTreeInfo(artwork.snd_first, 0);
2019 dumpTreeInfo(artwork.mus_first, 0);
2023 static void SaveUserLevelInfo()
2029 filename = getPath2(getUserLevelDir(getLoginName()), LEVELINFO_FILENAME);
2031 if (!(file = fopen(filename, MODE_WRITE)))
2033 Error(ERR_WARN, "cannot write level info file '%s'", filename);
2038 /* always start with reliable default values */
2039 setTreeInfoToDefaults(&ldi, TREE_TYPE_LEVEL_DIR);
2041 ldi.name = getStringCopy(getLoginName());
2042 ldi.author = getStringCopy(getRealName());
2044 ldi.first_level = 1;
2045 ldi.sort_priority = LEVELCLASS_USER_START;
2046 ldi.readonly = FALSE;
2047 ldi.graphics_set = getStringCopy(GRAPHICS_SUBDIR);
2048 ldi.sounds_set = getStringCopy(SOUNDS_SUBDIR);
2049 ldi.music_set = getStringCopy(MUSIC_SUBDIR);
2051 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2052 getCookie("LEVELINFO")));
2054 for (i=0; i<NUM_LEVELINFO_TOKENS; i++)
2055 if (i != LEVELINFO_TOKEN_IDENTIFIER &&
2056 i != LEVELINFO_TOKEN_NAME_SORTING &&
2057 i != LEVELINFO_TOKEN_IMPORTED_FROM)
2058 fprintf(file, "%s\n", getSetupLine(levelinfo_tokens, "", i));
2063 SetFilePermissions(filename, PERMS_PRIVATE);
2066 char *getSetupValue(int type, void *value)
2068 static char value_string[MAX_LINE_LEN];
2076 strcpy(value_string, (*(boolean *)value ? "true" : "false"));
2080 strcpy(value_string, (*(boolean *)value ? "on" : "off"));
2084 strcpy(value_string, (*(boolean *)value ? "yes" : "no"));
2088 strcpy(value_string, getKeyNameFromKey(*(Key *)value));
2092 strcpy(value_string, getX11KeyNameFromKey(*(Key *)value));
2096 sprintf(value_string, "%d", *(int *)value);
2100 strcpy(value_string, *(char **)value);
2104 value_string[0] = '\0';
2108 return value_string;
2111 char *getSetupLine(struct TokenInfo *token_info, char *prefix, int token_nr)
2115 static char token_string[MAX_LINE_LEN];
2116 int token_type = token_info[token_nr].type;
2117 void *setup_value = token_info[token_nr].value;
2118 char *token_text = token_info[token_nr].text;
2119 char *value_string = getSetupValue(token_type, setup_value);
2121 /* build complete token string */
2122 sprintf(token_string, "%s%s", prefix, token_text);
2124 /* build setup entry line */
2125 line = getFormattedSetupEntry(token_string, value_string);
2127 if (token_type == TYPE_KEY_X11)
2129 Key key = *(Key *)setup_value;
2130 char *keyname = getKeyNameFromKey(key);
2132 /* add comment, if useful */
2133 if (strcmp(keyname, "(undefined)") != 0 &&
2134 strcmp(keyname, "(unknown)") != 0)
2136 /* add at least one whitespace */
2138 for (i=strlen(line); i<TOKEN_COMMENT_POSITION; i++)
2142 strcat(line, keyname);
2149 void LoadLevelSetup_LastSeries()
2152 struct SetupFileList *level_setup_list = NULL;
2154 /* always start with reliable default values */
2155 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2157 /* ----------------------------------------------------------------------- */
2158 /* ~/.<program>/levelsetup.conf */
2159 /* ----------------------------------------------------------------------- */
2161 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2163 if ((level_setup_list = loadSetupFileList(filename)))
2165 char *last_level_series =
2166 getTokenValue(level_setup_list, TOKEN_STR_LAST_LEVEL_SERIES);
2168 leveldir_current = getTreeInfoFromIdentifier(leveldir_first,
2170 if (leveldir_current == NULL)
2171 leveldir_current = getFirstValidTreeInfoEntry(leveldir_first);
2173 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2175 freeSetupFileList(level_setup_list);
2178 Error(ERR_WARN, "using default setup values");
2183 void SaveLevelSetup_LastSeries()
2186 char *level_subdir = leveldir_current->filename;
2189 /* ----------------------------------------------------------------------- */
2190 /* ~/.<program>/levelsetup.conf */
2191 /* ----------------------------------------------------------------------- */
2193 InitUserDataDirectory();
2195 filename = getPath2(getSetupDir(), LEVELSETUP_FILENAME);
2197 if (!(file = fopen(filename, MODE_WRITE)))
2199 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2204 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2205 getCookie("LEVELSETUP")));
2206 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_LEVEL_SERIES,
2212 SetFilePermissions(filename, PERMS_PRIVATE);
2215 static void checkSeriesInfo()
2217 static char *level_directory = NULL;
2219 struct dirent *dir_entry;
2221 /* check for more levels besides the 'levels' field of 'levelinfo.conf' */
2223 level_directory = getPath2((leveldir_current->user_defined ?
2224 getUserLevelDir(NULL) :
2225 options.level_directory),
2226 leveldir_current->fullpath);
2228 if ((dir = opendir(level_directory)) == NULL)
2230 Error(ERR_WARN, "cannot read level directory '%s'", level_directory);
2234 while ((dir_entry = readdir(dir)) != NULL) /* last directory entry */
2236 if (strlen(dir_entry->d_name) > 4 &&
2237 dir_entry->d_name[3] == '.' &&
2238 strcmp(&dir_entry->d_name[4], LEVELFILE_EXTENSION) == 0)
2240 char levelnum_str[4];
2243 strncpy(levelnum_str, dir_entry->d_name, 3);
2244 levelnum_str[3] = '\0';
2246 levelnum_value = atoi(levelnum_str);
2248 if (levelnum_value < leveldir_current->first_level)
2250 Error(ERR_WARN, "additional level %d found", levelnum_value);
2251 leveldir_current->first_level = levelnum_value;
2253 else if (levelnum_value > leveldir_current->last_level)
2255 Error(ERR_WARN, "additional level %d found", levelnum_value);
2256 leveldir_current->last_level = levelnum_value;
2264 void LoadLevelSetup_SeriesInfo()
2267 struct SetupFileList *level_setup_list = NULL;
2268 char *level_subdir = leveldir_current->filename;
2270 /* always start with reliable default values */
2271 level_nr = leveldir_current->first_level;
2273 checkSeriesInfo(leveldir_current);
2275 /* ----------------------------------------------------------------------- */
2276 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2277 /* ----------------------------------------------------------------------- */
2279 level_subdir = leveldir_current->filename;
2281 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2283 if ((level_setup_list = loadSetupFileList(filename)))
2287 token_value = getTokenValue(level_setup_list, TOKEN_STR_LAST_PLAYED_LEVEL);
2291 level_nr = atoi(token_value);
2293 if (level_nr < leveldir_current->first_level)
2294 level_nr = leveldir_current->first_level;
2295 if (level_nr > leveldir_current->last_level)
2296 level_nr = leveldir_current->last_level;
2299 token_value = getTokenValue(level_setup_list, TOKEN_STR_HANDICAP_LEVEL);
2303 int level_nr = atoi(token_value);
2305 if (level_nr < leveldir_current->first_level)
2306 level_nr = leveldir_current->first_level;
2307 if (level_nr > leveldir_current->last_level + 1)
2308 level_nr = leveldir_current->last_level;
2310 if (leveldir_current->user_defined)
2311 level_nr = leveldir_current->last_level;
2313 leveldir_current->handicap_level = level_nr;
2316 checkSetupFileListIdentifier(level_setup_list, getCookie("LEVELSETUP"));
2318 freeSetupFileList(level_setup_list);
2321 Error(ERR_WARN, "using default setup values");
2326 void SaveLevelSetup_SeriesInfo()
2329 char *level_subdir = leveldir_current->filename;
2330 char *level_nr_str = int2str(level_nr, 0);
2331 char *handicap_level_str = int2str(leveldir_current->handicap_level, 0);
2334 /* ----------------------------------------------------------------------- */
2335 /* ~/.<program>/levelsetup/<level series>/levelsetup.conf */
2336 /* ----------------------------------------------------------------------- */
2338 InitLevelSetupDirectory(level_subdir);
2340 filename = getPath2(getLevelSetupDir(level_subdir), LEVELSETUP_FILENAME);
2342 if (!(file = fopen(filename, MODE_WRITE)))
2344 Error(ERR_WARN, "cannot write setup file '%s'", filename);
2349 fprintf(file, "%s\n\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2350 getCookie("LEVELSETUP")));
2351 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_LAST_PLAYED_LEVEL,
2353 fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_HANDICAP_LEVEL,
2354 handicap_level_str));
2359 SetFilePermissions(filename, PERMS_PRIVATE);